JAL-3210 Merge branch 'develop' into trialMerge
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 16 Jul 2019 07:14:32 +0000 (08:14 +0100)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 16 Jul 2019 07:14:32 +0000 (08:14 +0100)
Conflicts:
.classpath
.project
.settings/org.eclipse.jdt.core.prefs
.settings/org.eclipse.jdt.ui.prefs
build.xml
resources/lang/Messages_es.properties
src/MCview/AppletPDBCanvas.java
src/MCview/Atom.java
src/MCview/PDBCanvas.java
src/jalview/analysis/AAFrequency.java
src/jalview/appletgui/EmbmenuFrame.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/appletgui/SeqPanel.java
src/jalview/bin/Cache.java
src/jalview/bin/Jalview.java
src/jalview/datamodel/DBRefEntry.java
src/jalview/datamodel/Sequence.java
src/jalview/ext/ensembl/EnsemblMap.java
src/jalview/ext/paradise/Annotate3D.java
src/jalview/fts/service/pdb/PDBFTSRestClient.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/FeatureTypeSettings.java
src/jalview/gui/WebserviceInfo.java
src/jalview/io/BackupFiles.java
src/jalview/io/FileLoader.java
src/jalview/jbgui/GDesktop.java
src/jalview/jbgui/GPreferences.java
src/jalview/project/Jalview2XML.java
src/jalview/util/ImageMaker.java
src/jalview/util/Platform.java
src/jalview/ws/SequenceFetcher.java

736 files changed:
.classpath
.project
.settings/org.eclipse.buildship.core.prefs [new file with mode: 0644]
.settings/org.eclipse.jdt.core.jalview.prefs [new file with mode: 0644]
.settings/org.eclipse.jdt.core.prefs
.settings/org.eclipse.jdt.ui.prefs
JAVA-11-README [new file with mode: 0644]
THIRDPARTYLIBS
authors.props [new file with mode: 0644]
build.gradle [new file with mode: 0644]
doc/NEW_JARS [new file with mode: 0644]
doc/building.html [deleted file]
doc/building.md [new file with mode: 0644]
doc/convert.sh [new file with mode: 0644]
doc/getdown_installation_structure.pdf [new file with mode: 0644]
doc/github.css [new file with mode: 0644]
examples/exampleFile.jvp [new file with mode: 0644]
getdown/lib/getdown-core.jar [new file with mode: 0644]
getdown/lib/getdown-launcher-local.jar [new file with mode: 0644]
getdown/lib/getdown-launcher.jar [new file with mode: 0644]
getdown/src/getdown/AUTHORS [new file with mode: 0644]
getdown/src/getdown/CHANGELOG.md [new file with mode: 0644]
getdown/src/getdown/LICENSE [new file with mode: 0644]
getdown/src/getdown/README.md [new file with mode: 0644]
getdown/src/getdown/ant/.project-MOVED [new file with mode: 0644]
getdown/src/getdown/ant/.settings/org.eclipse.core.resources.prefs [new file with mode: 0644]
getdown/src/getdown/ant/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
getdown/src/getdown/ant/.settings/org.eclipse.m2e.core.prefs [new file with mode: 0644]
getdown/src/getdown/ant/pom.xml [new file with mode: 0644]
getdown/src/getdown/ant/src/main/java/com/threerings/getdown/tools/DigesterTask.java [new file with mode: 0644]
getdown/src/getdown/bin/differ [new file with mode: 0755]
getdown/src/getdown/bin/patcher [new file with mode: 0755]
getdown/src/getdown/core/.project-MOVED [new file with mode: 0644]
getdown/src/getdown/core/.settings/org.eclipse.core.resources.prefs [new file with mode: 0644]
getdown/src/getdown/core/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
getdown/src/getdown/core/.settings/org.eclipse.m2e.core.prefs [new file with mode: 0644]
getdown/src/getdown/core/pom.xml [new file with mode: 0644]
getdown/src/getdown/core/src/it/java/com/threerings/getdown/tests/DigesterIT.java [new file with mode: 0644]
getdown/src/getdown/core/src/it/resources/testapp/background.png [new file with mode: 0644]
getdown/src/getdown/core/src/it/resources/testapp/crazyhashfile#txt [new file with mode: 0644]
getdown/src/getdown/core/src/it/resources/testapp/foo.jar [new file with mode: 0644]
getdown/src/getdown/core/src/it/resources/testapp/funny%test dir/some=file.txt [new file with mode: 0644]
getdown/src/getdown/core/src/it/resources/testapp/getdown.txt [new file with mode: 0644]
getdown/src/getdown/core/src/it/resources/testapp/script.sh [new file with mode: 0644]
getdown/src/getdown/core/src/it/resources/testapp/testapp.jar [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/Log.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/ResourceCache.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Build.java.tmpl [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/ClassPath.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Digest.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/PathBuilder.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Properties.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Resource.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/SysProps.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/Downloader.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Differ.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiff.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffCodes.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffPatcher.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Patcher.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Base64.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Color.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Config.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/FileUtil.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/HostWhitelist.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/MessageUtil.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressAggregator.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressObserver.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Rectangle.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StreamUtil.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StringUtil.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/VersionUtil.java [new file with mode: 0644]
getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/GarbageCollectorTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/ResourceCacheTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/ClassPathTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/EnvConfigTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/PathBuilderTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/SysPropsTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/ColorTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/ConfigTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/FileUtilTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/HostWhitelistTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/StringUtilTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/VersionUtilTest.java [new file with mode: 0644]
getdown/src/getdown/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker [new file with mode: 0644]
getdown/src/getdown/launcher/.project-MOVED [new file with mode: 0644]
getdown/src/getdown/launcher/.settings/org.eclipse.core.resources.prefs [new file with mode: 0644]
getdown/src/getdown/launcher/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
getdown/src/getdown/launcher/.settings/org.eclipse.m2e.core.prefs [new file with mode: 0644]
getdown/src/getdown/launcher/pom.xml [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/AbortPanel.java [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/MultipleGetdownRunning.java [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/java/jalview/bin/StartupNotificationListener.java [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages.properties [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_de.properties [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_es.properties [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_it.properties [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties [new file with mode: 0644]
getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties [new file with mode: 0644]
getdown/src/getdown/lib/SOURCE_HEADER [new file with mode: 0644]
getdown/src/getdown/lib/commons-compress-1.18.jar [new file with mode: 0644]
getdown/src/getdown/lib/jRegistryKey.dll [new file with mode: 0644]
getdown/src/getdown/lib/jregistrykey/jregistrykey/1.0/jregistrykey-1.0.jar [new file with mode: 0644]
getdown/src/getdown/lib/jregistrykey/jregistrykey/1.0/jregistrykey-1.0.pom [new file with mode: 0644]
getdown/src/getdown/lib/jregistrykey/jregistrykey/maven-metadata-local.xml [new file with mode: 0644]
getdown/src/getdown/lib/manifest.mf [new file with mode: 0644]
getdown/src/getdown/mvn_cmd [new file with mode: 0755]
getdown/src/getdown/pom.xml [new file with mode: 0644]
gradle.properties [new file with mode: 0644]
help/help/help.hs [moved from help/help.hs with 100% similarity]
help/help/help.jhm [moved from help/help.jhm with 98% similarity]
help/help/helpTOC.xml [moved from help/helpTOC.xml with 95% similarity]
help/help/html/.cvsignore [moved from help/html/.cvsignore with 100% similarity]
help/help/html/Jalview_Logo.png [moved from help/html/Jalview_Logo.png with 100% similarity]
help/help/html/align.jpg [moved from help/html/align.jpg with 100% similarity]
help/help/html/calculations/calculatedialog.png [moved from help/html/calculations/calculatedialog.png with 100% similarity]
help/help/html/calculations/calculations.html [moved from help/html/calculations/calculations.html with 100% similarity]
help/help/html/calculations/consensus.html [moved from help/html/calculations/consensus.html with 100% similarity]
help/help/html/calculations/conservation.html [moved from help/html/calculations/conservation.html with 100% similarity]
help/help/html/calculations/pairwise.html [moved from help/html/calculations/pairwise.html with 100% similarity]
help/help/html/calculations/pca.html [moved from help/html/calculations/pca.html with 94% similarity]
help/help/html/calculations/pcaviewer.png [new file with mode: 0644]
help/help/html/calculations/quality.html [moved from help/html/calculations/quality.html with 100% similarity]
help/help/html/calculations/recoverInputdata.html [moved from help/html/calculations/recoverInputdata.html with 100% similarity]
help/help/html/calculations/redundancy.html [moved from help/html/calculations/redundancy.html with 100% similarity]
help/help/html/calculations/referenceseq.html [moved from help/html/calculations/referenceseq.html with 100% similarity]
help/help/html/calculations/scorematrices.html [moved from help/html/calculations/scorematrices.html with 100% similarity]
help/help/html/calculations/sorting.html [moved from help/html/calculations/sorting.html with 100% similarity]
help/help/html/calculations/structureconsensus.html [moved from help/html/calculations/structureconsensus.html with 100% similarity]
help/help/html/calculations/tree.html [moved from help/html/calculations/tree.html with 100% similarity]
help/help/html/calculations/treeviewer.html [moved from help/html/calculations/treeviewer.html with 88% similarity]
help/help/html/colourSchemes/.cvsignore [moved from help/html/colourSchemes/.cvsignore with 100% similarity]
help/help/html/colourSchemes/abovePID.html [moved from help/html/colourSchemes/abovePID.html with 100% similarity]
help/help/html/colourSchemes/annotationColourSetting.gif [moved from help/html/colourSchemes/annotationColourSetting.gif with 100% similarity]
help/help/html/colourSchemes/annotationColouring.html [moved from help/html/colourSchemes/annotationColouring.html with 100% similarity]
help/help/html/colourSchemes/annotationColours.gif [moved from help/html/colourSchemes/annotationColours.gif with 100% similarity]
help/help/html/colourSchemes/blosum.html [moved from help/html/colourSchemes/blosum.html with 100% similarity]
help/help/html/colourSchemes/buried.html [moved from help/html/colourSchemes/buried.html with 100% similarity]
help/help/html/colourSchemes/clustal.html [moved from help/html/colourSchemes/clustal.html with 100% similarity]
help/help/html/colourSchemes/colbytcoffee.png [moved from help/html/colourSchemes/colbytcoffee.png with 100% similarity]
help/help/html/colourSchemes/conservation.html [moved from help/html/colourSchemes/conservation.html with 100% similarity]
help/help/html/colourSchemes/helix.html [moved from help/html/colourSchemes/helix.html with 100% similarity]
help/help/html/colourSchemes/hydrophobic.html [moved from help/html/colourSchemes/hydrophobic.html with 100% similarity]
help/help/html/colourSchemes/index.html [new file with mode: 0755]
help/help/html/colourSchemes/nucleotide.html [moved from help/html/colourSchemes/nucleotide.html with 100% similarity]
help/help/html/colourSchemes/pid.html [moved from help/html/colourSchemes/pid.html with 100% similarity]
help/help/html/colourSchemes/purinepyrimidine.html [moved from help/html/colourSchemes/purinepyrimidine.html with 100% similarity]
help/help/html/colourSchemes/rnahelicesColouring.html [moved from help/html/colourSchemes/rnahelicesColouring.html with 100% similarity]
help/help/html/colourSchemes/rnahelicescoloring.png [moved from help/html/colourSchemes/rnahelicescoloring.png with 100% similarity]
help/help/html/colourSchemes/strand.html [moved from help/html/colourSchemes/strand.html with 100% similarity]
help/help/html/colourSchemes/taylor.html [moved from help/html/colourSchemes/taylor.html with 100% similarity]
help/help/html/colourSchemes/textcolour.gif [moved from help/html/colourSchemes/textcolour.gif with 100% similarity]
help/help/html/colourSchemes/textcolour.html [moved from help/html/colourSchemes/textcolour.html with 100% similarity]
help/help/html/colourSchemes/turn.html [moved from help/html/colourSchemes/turn.html with 100% similarity]
help/help/html/colourSchemes/user.html [moved from help/html/colourSchemes/user.html with 100% similarity]
help/help/html/colourSchemes/userDefined.gif [moved from help/html/colourSchemes/userDefined.gif with 100% similarity]
help/help/html/colourSchemes/userDefined_java7.gif [moved from help/html/colourSchemes/userDefined_java7.gif with 100% similarity]
help/help/html/colourSchemes/zappo.html [moved from help/html/colourSchemes/zappo.html with 100% similarity]
help/help/html/editing/.cvsignore [moved from help/html/editing/.cvsignore with 100% similarity]
help/help/html/editing/editing.jpg [moved from help/html/editing/editing.jpg with 100% similarity]
help/help/html/editing/index.html [moved from help/html/editing/index.html with 100% similarity]
help/help/html/features/.cvsignore [moved from help/html/features/.cvsignore with 100% similarity]
help/help/html/features/AnnotationColumnSelectionWithSM.png [moved from help/html/features/AnnotationColumnSelectionWithSM.png with 100% similarity]
help/help/html/features/AnnotationColumnSelectionWithoutSM.gif [moved from help/html/features/AnnotationColumnSelectionWithoutSM.gif with 100% similarity]
help/help/html/features/amendfeature.gif [moved from help/html/features/amendfeature.gif with 100% similarity]
help/help/html/features/annotation.html [moved from help/html/features/annotation.html with 100% similarity]
help/help/html/features/annotationsFormat.html [moved from help/html/features/annotationsFormat.html with 100% similarity]
help/help/html/features/bioJsonFormat.html [moved from help/html/features/bioJsonFormat.html with 100% similarity]
help/help/html/features/biojsmsa.html [moved from help/html/features/biojsmsa.html with 100% similarity]
help/help/html/features/chimera.html [moved from help/html/features/chimera.html with 100% similarity]
help/help/html/features/clarguments.html [moved from help/html/features/clarguments.html with 90% similarity]
help/help/html/features/codingfeatures.html [moved from help/html/features/codingfeatures.html with 100% similarity]
help/help/html/features/columnFilterByAnnotation.html [moved from help/html/features/columnFilterByAnnotation.html with 100% similarity]
help/help/html/features/commandline.html [new file with mode: 0644]
help/help/html/features/creatinFeatures.html [moved from help/html/features/creatinFeatures.html with 100% similarity]
help/help/html/features/crnewfeature.gif [moved from help/html/features/crnewfeature.gif with 100% similarity]
help/help/html/features/cursorMode.html [moved from help/html/features/cursorMode.html with 100% similarity]
help/help/html/features/editingFeatures.html [moved from help/html/features/editingFeatures.html with 100% similarity]
help/help/html/features/ensemblsequencefetcher.html [moved from help/html/features/ensemblsequencefetcher.html with 100% similarity]
help/help/html/features/fcsgtthan.gif [moved from help/html/features/fcsgtthan.gif with 100% similarity]
help/help/html/features/fcsltthan.gif [moved from help/html/features/fcsltthan.gif with 100% similarity]
help/help/html/features/fcsminmax.gif [moved from help/html/features/fcsminmax.gif with 100% similarity]
help/help/html/features/fcsntlabel.gif [moved from help/html/features/fcsntlabel.gif with 100% similarity]
help/help/html/features/featureDisplaySettings.png [new file with mode: 0644]
help/help/html/features/featureSettings.png [new file with mode: 0644]
help/help/html/features/featuresFormat.html [moved from help/html/features/featuresFormat.html with 58% similarity]
help/help/html/features/featureschemes.html [new file with mode: 0644]
help/help/html/features/featuresettings.html [moved from help/html/features/featuresettings.html with 52% similarity]
help/help/html/features/groovy.html [moved from help/html/features/groovy.html with 100% similarity]
help/help/html/features/hiddenRegions.html [moved from help/html/features/hiddenRegions.html with 100% similarity]
help/help/html/features/importvcf.html [new file with mode: 0755]
help/help/html/features/jalarchive.html [new file with mode: 0755]
help/help/html/features/jmol.html [moved from help/html/features/jmol.html with 100% similarity]
help/help/html/features/jvlfiles.html [new file with mode: 0644]
help/help/html/features/mmcif.html [moved from help/html/features/mmcif.html with 100% similarity]
help/help/html/features/multipleViews.html [moved from help/html/features/multipleViews.html with 100% similarity]
help/help/html/features/newkeystrokes.html [moved from help/html/features/newkeystrokes.html with 100% similarity]
help/help/html/features/overview.html [moved from help/html/features/overview.html with 100% similarity]
help/help/html/features/overview.png [moved from help/html/features/overview.png with 100% similarity]
help/help/html/features/pdbseqfetcher.png [moved from help/html/features/pdbseqfetcher.png with 100% similarity]
help/help/html/features/pdbsequencefetcher.html [moved from help/html/features/pdbsequencefetcher.html with 100% similarity]
help/help/html/features/pdbviewer.html [moved from help/html/features/pdbviewer.html with 100% similarity]
help/help/html/features/preferences.html [moved from help/html/features/preferences.html with 90% similarity]
help/help/html/features/schooser_enter-id.png [moved from help/html/features/schooser_enter-id.png with 100% similarity]
help/help/html/features/schooser_main.png [moved from help/html/features/schooser_main.png with 100% similarity]
help/help/html/features/search.html [moved from help/html/features/search.html with 100% similarity]
help/help/html/features/search.png [moved from help/html/features/search.png with 100% similarity]
help/help/html/features/searchclearhist.png [moved from help/html/features/searchclearhist.png with 100% similarity]
help/help/html/features/searchhist.png [moved from help/html/features/searchhist.png with 100% similarity]
help/help/html/features/selectfetchdb.gif [moved from help/html/features/selectfetchdb.gif with 100% similarity]
help/help/html/features/seqfeatures.html [moved from help/html/features/seqfeatures.html with 100% similarity]
help/help/html/features/seqfetch.html [moved from help/html/features/seqfetch.html with 100% similarity]
help/help/html/features/seqfetcher.gif [moved from help/html/features/seqfetcher.gif with 100% similarity]
help/help/html/features/seqmappings.html [moved from help/html/features/seqmappings.html with 100% similarity]
help/help/html/features/sifts_mapping_output.png [moved from help/html/features/sifts_mapping_output.png with 100% similarity]
help/help/html/features/siftsmapping.html [moved from help/html/features/siftsmapping.html with 100% similarity]
help/help/html/features/splitView.html [moved from help/html/features/splitView.html with 100% similarity]
help/help/html/features/structurechooser.html [moved from help/html/features/structurechooser.html with 100% similarity]
help/help/html/features/uniprotqueryfields.html [moved from help/html/features/uniprotqueryfields.html with 100% similarity]
help/help/html/features/uniprotseqfetcher.png [moved from help/html/features/uniprotseqfetcher.png with 100% similarity]
help/help/html/features/uniprotsequencefetcher.html [moved from help/html/features/uniprotsequencefetcher.html with 100% similarity]
help/help/html/features/varna.html [moved from help/html/features/varna.html with 100% similarity]
help/help/html/features/viewingpdbs.html [moved from help/html/features/viewingpdbs.html with 100% similarity]
help/help/html/features/wrap.html [moved from help/html/features/wrap.html with 100% similarity]
help/help/html/features/xsspannotation.html [moved from help/html/features/xsspannotation.html with 100% similarity]
help/help/html/groovy/featuresCounter.html [moved from help/html/groovy/featuresCounter.html with 100% similarity]
help/help/html/index.html [moved from help/html/index.html with 100% similarity]
help/help/html/io/.cvsignore [moved from help/html/io/.cvsignore with 100% similarity]
help/help/html/io/export.html [moved from help/html/io/export.html with 95% similarity]
help/help/html/io/exportseqreport.html [moved from help/html/io/exportseqreport.html with 100% similarity]
help/help/html/io/file.png [moved from help/html/io/file.png with 100% similarity]
help/help/html/io/fileformats.html [moved from help/html/io/fileformats.html with 100% similarity]
help/help/html/io/index.html [moved from help/html/io/index.html with 100% similarity]
help/help/html/io/modellerpir.html [moved from help/html/io/modellerpir.html with 100% similarity]
help/help/html/io/seqreport.gif [moved from help/html/io/seqreport.gif with 100% similarity]
help/help/html/io/tcoffeescores.html [moved from help/html/io/tcoffeescores.html with 100% similarity]
help/help/html/keys.html [moved from help/html/keys.html with 100% similarity]
help/help/html/memory.html [new file with mode: 0755]
help/help/html/menus/alignmentMenu.html [moved from help/html/menus/alignmentMenu.html with 99% similarity]
help/help/html/menus/alwannotation.html [moved from help/html/menus/alwannotation.html with 100% similarity]
help/help/html/menus/alwannotationpanel.html [moved from help/html/menus/alwannotationpanel.html with 100% similarity]
help/help/html/menus/alwcalculate.html [moved from help/html/menus/alwcalculate.html with 91% similarity]
help/help/html/menus/alwcolour.html [moved from help/html/menus/alwcolour.html with 100% similarity]
help/help/html/menus/alwedit.html [moved from help/html/menus/alwedit.html with 100% similarity]
help/help/html/menus/alwfile.html [moved from help/html/menus/alwfile.html with 95% similarity]
help/help/html/menus/alwformat.html [moved from help/html/menus/alwformat.html with 100% similarity]
help/help/html/menus/alwselect.html [moved from help/html/menus/alwselect.html with 100% similarity]
help/help/html/menus/alwview.html [moved from help/html/menus/alwview.html with 100% similarity]
help/help/html/menus/desktopMenu.html [moved from help/html/menus/desktopMenu.html with 80% similarity]
help/help/html/menus/index.html [moved from help/html/menus/index.html with 100% similarity]
help/help/html/menus/popupMenu.html [moved from help/html/menus/popupMenu.html with 100% similarity]
help/help/html/menus/wsmenu.html [moved from help/html/menus/wsmenu.html with 100% similarity]
help/help/html/misc/.cvsignore [moved from help/html/misc/.cvsignore with 100% similarity]
help/help/html/misc/aaproperties.html [moved from help/html/misc/aaproperties.html with 100% similarity]
help/help/html/misc/ala.jpg [moved from help/html/misc/ala.jpg with 100% similarity]
help/help/html/misc/aminoAcids.html [moved from help/html/misc/aminoAcids.html with 100% similarity]
help/help/html/misc/arg.jpg [moved from help/html/misc/arg.jpg with 100% similarity]
help/help/html/misc/asparagine.jpg [moved from help/html/misc/asparagine.jpg with 100% similarity]
help/help/html/misc/aspartate.jpg [moved from help/html/misc/aspartate.jpg with 100% similarity]
help/help/html/misc/cys.jpg [moved from help/html/misc/cys.jpg with 100% similarity]
help/help/html/misc/geneticCode.html [moved from help/html/misc/geneticCode.html with 100% similarity]
help/help/html/misc/glutamate.jpg [moved from help/html/misc/glutamate.jpg with 100% similarity]
help/help/html/misc/glutamine.jpg [moved from help/html/misc/glutamine.jpg with 100% similarity]
help/help/html/misc/gly.jpg [moved from help/html/misc/gly.jpg with 100% similarity]
help/help/html/misc/his.jpg [moved from help/html/misc/his.jpg with 100% similarity]
help/help/html/misc/iso.jpg [moved from help/html/misc/iso.jpg with 100% similarity]
help/help/html/misc/leu.jpg [moved from help/html/misc/leu.jpg with 100% similarity]
help/help/html/misc/lys.jpg [moved from help/html/misc/lys.jpg with 100% similarity]
help/help/html/misc/met.jpg [moved from help/html/misc/met.jpg with 100% similarity]
help/help/html/misc/phe.jpg [moved from help/html/misc/phe.jpg with 100% similarity]
help/help/html/misc/pro.jpg [moved from help/html/misc/pro.jpg with 100% similarity]
help/help/html/misc/properties.gif [moved from help/html/misc/properties.gif with 100% similarity]
help/help/html/misc/ser.jpg [moved from help/html/misc/ser.jpg with 100% similarity]
help/help/html/misc/thr.jpg [moved from help/html/misc/thr.jpg with 100% similarity]
help/help/html/misc/tryp.jpg [moved from help/html/misc/tryp.jpg with 100% similarity]
help/help/html/misc/tyr.jpg [moved from help/html/misc/tyr.jpg with 100% similarity]
help/help/html/misc/val.jpg [moved from help/html/misc/val.jpg with 100% similarity]
help/help/html/na/index.html [moved from help/html/na/index.html with 100% similarity]
help/help/html/privacy.html [moved from help/html/privacy.html with 95% similarity]
help/help/html/releases.html [moved from help/html/releases.html with 90% similarity]
help/help/html/webServices/.cvsignore [moved from help/html/webServices/.cvsignore with 100% similarity]
help/help/html/webServices/AACon.html [moved from help/html/webServices/AACon.html with 100% similarity]
help/help/html/webServices/JABAWS.html [moved from help/html/webServices/JABAWS.html with 100% similarity]
help/help/html/webServices/RNAalifold.html [moved from help/html/webServices/RNAalifold.html with 100% similarity]
help/help/html/webServices/RNAalifoldAnnotationRows.png [moved from help/html/webServices/RNAalifoldAnnotationRows.png with 100% similarity]
help/help/html/webServices/clwqueued.gif [moved from help/html/webServices/clwqueued.gif with 100% similarity]
help/help/html/webServices/dbreffetcher.html [moved from help/html/webServices/dbreffetcher.html with 100% similarity]
help/help/html/webServices/index.html [moved from help/html/webServices/index.html with 100% similarity]
help/help/html/webServices/invalidurldialog.gif [moved from help/html/webServices/invalidurldialog.gif with 100% similarity]
help/help/html/webServices/jalviewrssreader.gif [moved from help/html/webServices/jalviewrssreader.gif with 100% similarity]
help/help/html/webServices/jnet.html [moved from help/html/webServices/jnet.html with 100% similarity]
help/help/html/webServices/jnetprediction.gif [moved from help/html/webServices/jnetprediction.gif with 100% similarity]
help/help/html/webServices/msaclient.html [moved from help/html/webServices/msaclient.html with 100% similarity]
help/help/html/webServices/multimafftjbs.gif [moved from help/html/webServices/multimafftjbs.gif with 100% similarity]
help/help/html/webServices/newsreader.html [moved from help/html/webServices/newsreader.html with 100% similarity]
help/help/html/webServices/proteinDisorder.html [moved from help/html/webServices/proteinDisorder.html with 100% similarity]
help/help/html/webServices/shmr.html [moved from help/html/webServices/shmr.html with 100% similarity]
help/help/html/webServices/urllinks.html [moved from help/html/webServices/urllinks.html with 100% similarity]
help/help/html/webServices/webServicesParams.html [moved from help/html/webServices/webServicesParams.html with 100% similarity]
help/help/html/webServices/webServicesPrefs.html [moved from help/html/webServices/webServicesPrefs.html with 100% similarity]
help/help/html/webServices/wsparams.gif [moved from help/html/webServices/wsparams.gif with 100% similarity]
help/help/html/webServices/wsprefs.gif [moved from help/html/webServices/wsprefs.gif with 100% similarity]
help/help/html/whatsNew.html [new file with mode: 0755]
help/help/icons/Home.png [moved from help/icons/Home.png with 100% similarity]
help/help/icons/back.png [moved from help/icons/back.png with 100% similarity]
help/help/icons/forward.png [moved from help/icons/forward.png with 100% similarity]
help/help/icons/print.png [moved from help/icons/print.png with 100% similarity]
help/help/icons/setup.png [moved from help/icons/setup.png with 100% similarity]
help/html/calculations/pcaviewer.gif [deleted file]
help/html/colourSchemes/index.html [deleted file]
help/html/features/commandline.html [deleted file]
help/html/features/featureSettings.gif [deleted file]
help/html/features/featurecoloursettings.gif [deleted file]
help/html/features/featureschemes.html [deleted file]
help/html/features/jalarchive.html [deleted file]
help/html/memory.html [deleted file]
help/html/vamsas/index.html [deleted file]
help/html/whatsNew.html [deleted file]
j11lib/FastInfoset.jar [new file with mode: 0644]
j11lib/JGoogleAnalytics_0.3.jar [moved from lib/JGoogleAnalytics_0.3.jar with 100% similarity]
j11lib/VARNAv3-93.jar [moved from lib/VARNAv3-93.jar with 100% similarity]
j11lib/VAqua5-patch.jar [moved from lib/VAqua5-patch.jar with 100% similarity]
j11lib/apache-mime4j-0.6.jar [moved from lib/apache-mime4j-0.6.jar with 100% similarity]
j11lib/axis.jar [moved from lib/axis.jar with 100% similarity]
j11lib/biojava-core-4.1.0.jar [moved from lib/biojava-core-4.1.0.jar with 100% similarity]
j11lib/biojava-ontology-4.1.0.jar [moved from lib/biojava-ontology-4.1.0.jar with 100% similarity]
j11lib/commons-codec-1.3.jar [moved from lib/commons-codec-1.3.jar with 100% similarity]
j11lib/commons-compress-1.18.jar [new file with mode: 0644]
j11lib/commons-discovery.jar [moved from lib/commons-discovery.jar with 100% similarity]
j11lib/commons-logging-1.1.1.jar [moved from lib/commons-logging-1.1.1.jar with 100% similarity]
j11lib/getdown-core.jar [new file with mode: 0644]
j11lib/gmbal-api-only-MODULE.jar [new file with mode: 0644]
j11lib/groovy-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-ant-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-bsf-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-cli-commons-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-cli-picocli-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-console-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-datetime-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-dateutil-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-docgenerator-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-groovydoc-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-groovysh-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-jaxb-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-jmx-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-json-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-json-direct-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-jsr223-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-macro-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-nio-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-servlet-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-sql-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-swing-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-templates-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-test-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-test-junit5-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-testng-2.5.7.jar [new file with mode: 0644]
j11lib/groovy-xml-2.5.7.jar [new file with mode: 0644]
j11lib/htsjdk-2.12.0.jar [moved from lib/htsjdk-2.12.0.jar with 100% similarity]
j11lib/httpclient-4.0.3.jar [moved from lib/httpclient-4.0.3.jar with 100% similarity]
j11lib/httpcore-4.0.1.jar [moved from lib/httpcore-4.0.1.jar with 100% similarity]
j11lib/httpmime-4.0.3.jar [moved from lib/httpmime-4.0.3.jar with 100% similarity]
j11lib/intervalstore-v1.0.jar [moved from lib/intervalstore-v1.0.jar with 100% similarity]
j11lib/istack-commons-runtime.jar [new file with mode: 0644]
j11lib/jabaws-min-client-2.2.0.jar [moved from lib/jabaws-min-client-2.2.0.jar with 100% similarity]
j11lib/java-json.jar [moved from lib/java-json.jar with 100% similarity]
j11lib/javax.activation-api-1.2.0.jar [new file with mode: 0644]
j11lib/javax.annotation-api-1.3.2.jar [new file with mode: 0644]
j11lib/javax.jws-api-1.1.jar [new file with mode: 0644]
j11lib/javax.servlet-api-4.0.1.jar [new file with mode: 0644]
j11lib/javax.xml.rpc-api-1.1.2.jar [new file with mode: 0644]
j11lib/javax.xml.soap-api-1.4.0.jar [new file with mode: 0644]
j11lib/jaxb-api-2.3.1.jar [new file with mode: 0644]
j11lib/jaxb-runtime-2.3.2.jar [new file with mode: 0644]
j11lib/jaxws-api-2.3.1.jar [new file with mode: 0644]
j11lib/jaxws-rt-java9.jar [new file with mode: 0644]
j11lib/jersey-client-1.19.4.jar [moved from lib/jersey-client-1.19.jar with 82% similarity]
j11lib/jersey-core-1.19.4.jar [moved from lib/jersey-core-1.19.jar with 85% similarity]
j11lib/jersey-json-1.19.4.jar [moved from lib/jersey-json-1.19.jar with 85% similarity]
j11lib/jetty-http-9.2.10.v20150310.jar [moved from lib/jetty-http-9.2.10.v20150310.jar with 100% similarity]
j11lib/jetty-io-9.2.10.v20150310.jar [moved from lib/jetty-io-9.2.10.v20150310.jar with 100% similarity]
j11lib/jetty-server-9.2.10.v20150310.jar [moved from lib/jetty-server-9.2.10.v20150310.jar with 100% similarity]
j11lib/jetty-util-9.2.10.v20150310.jar [moved from lib/jetty-util-9.2.10.v20150310.jar with 100% similarity]
j11lib/jfreesvg-2.1.jar [moved from lib/jfreesvg-2.1.jar with 100% similarity]
j11lib/jhall.jar [moved from lib/jhall.jar with 100% similarity]
j11lib/json_simple-1.1.jar [moved from lib/json_simple-1.1.jar with 100% similarity]
j11lib/jsoup-1.8.1.jar [moved from lib/jsoup-1.8.1.jar with 100% similarity]
j11lib/jsr311-api-1.1.1.jar [moved from lib/jsr311-api-1.1.1.jar with 100% similarity]
j11lib/jswingreader-0.3.jar [moved from lib/jswingreader-0.3.jar with 100% similarity]
j11lib/libquaqua-8.0.jnilib.jar [moved from lib/libquaqua-8.0.jnilib.jar with 100% similarity]
j11lib/libquaqua64-8.0.jnilib.jar [moved from lib/libquaqua64-8.0.jnilib.jar with 100% similarity]
j11lib/log4j-to-slf4j-2.0-rc2.jar [moved from lib/log4j-to-slf4j-2.0-rc2.jar with 100% similarity]
j11lib/mail-MODULE.jar [new file with mode: 0644]
j11lib/miglayout-4.0-swing.jar [moved from lib/miglayout-4.0-swing.jar with 100% similarity]
j11lib/mimepull-1.9.11.jar [new file with mode: 0644]
j11lib/policy-2.7.6.jar [new file with mode: 0644]
j11lib/quaqua-filechooser-only-8.0.jar [moved from lib/quaqua-filechooser-only-8.0.jar with 100% similarity]
j11lib/regex.jar [moved from lib/regex.jar with 100% similarity]
j11lib/saaj-impl.jar [new file with mode: 0644]
j11lib/slf4j-api-1.7.26.jar [new file with mode: 0644]
j11lib/slf4j-log4j12-1.7.26.jar [new file with mode: 0644]
j11lib/stax-ex-1.8.1.jar [new file with mode: 0644]
j11lib/stax2-api-4.2.jar [new file with mode: 0644]
j11lib/streambuffer-1.5.7.jar [new file with mode: 0644]
j11lib/txw2-2.3.2.jar [new file with mode: 0644]
j11lib/vamsas-client.jar [moved from lib/vamsas-client.jar with 100% similarity]
j11lib/wsdl4j-1.6.3.jar [new file with mode: 0644]
j11lib/xercesImpl.jar [moved from lib/xercesImpl.jar with 100% similarity]
j11mod/FastInfoset.jar [new file with mode: 0644]
j11mod/getdown-launcher.jar [new file with mode: 0644]
j11mod/istack-commons-runtime.jar [new file with mode: 0644]
j11mod/javax.activation-MODULE.jar [new file with mode: 0644]
j11mod/javax.annotation-api-MODULE.jar [new file with mode: 0644]
j11mod/javax.jws-api-1.1.jar [new file with mode: 0644]
j11mod/javax.servlet-api-MODULE.jar [new file with mode: 0644]
j11mod/javax.ws.rs-api-2.1.1.jar [new file with mode: 0644]
j11mod/javax.xml.rpc-api-1.1.2.jar [new file with mode: 0644]
j11mod/javax.xml.soap-api.jar [new file with mode: 0644]
j11mod/jaxb-api-java9.jar [new file with mode: 0644]
j11mod/jaxb-runtime.jar [new file with mode: 0644]
j11mod/jaxws-api.jar [new file with mode: 0644]
j11mod/mimepull.jar [new file with mode: 0644]
j11mod/policy.jar [new file with mode: 0644]
j11mod/saaj-impl.jar [new file with mode: 0644]
j11mod/stax-ex.jar [new file with mode: 0644]
j11mod/stax2-api-MODULE.jar [new file with mode: 0644]
j11mod/streambuffer.jar [new file with mode: 0644]
j11mod/txw2.jar [new file with mode: 0644]
j11mod/wsdl4j-MODULE.jar [new file with mode: 0644]
j8lib/JGoogleAnalytics_0.3.jar [new file with mode: 0644]
j8lib/VARNAv3-93.jar [new file with mode: 0644]
j8lib/VAqua5-patch.jar [new file with mode: 0644]
j8lib/activation.jar [moved from lib/activation.jar with 100% similarity]
j8lib/apache-mime4j-0.6.jar [new file with mode: 0644]
j8lib/axis.jar [new file with mode: 0755]
j8lib/biojava-core-4.1.0.jar [new file with mode: 0644]
j8lib/biojava-ontology-4.1.0.jar [new file with mode: 0644]
j8lib/commons-codec-1.3.jar [new file with mode: 0644]
j8lib/commons-compress-1.18.jar [new file with mode: 0644]
j8lib/commons-discovery.jar [new file with mode: 0755]
j8lib/commons-logging-1.1.1.jar [new file with mode: 0644]
j8lib/getdown-core.jar [new file with mode: 0644]
j8lib/groovy-all-2.4.12-indy.jar [moved from lib/groovy-all-2.4.12-indy.jar with 100% similarity]
j8lib/htsjdk-2.12.0.jar [new file with mode: 0644]
j8lib/httpclient-4.0.3.jar [new file with mode: 0644]
j8lib/httpcore-4.0.1.jar [new file with mode: 0644]
j8lib/httpmime-4.0.3.jar [new file with mode: 0644]
j8lib/intervalstore-v1.0.jar [new file with mode: 0644]
j8lib/jabaws-min-client-2.2.0.jar [new file with mode: 0644]
j8lib/java-json.jar [new file with mode: 0755]
j8lib/jaxrpc.jar [moved from lib/jaxrpc.jar with 100% similarity]
j8lib/jersey-client-1.19.4.jar [new file with mode: 0644]
j8lib/jersey-core-1.19.4.jar [new file with mode: 0644]
j8lib/jersey-json-1.19.4.jar [new file with mode: 0644]
j8lib/jetty-http-9.2.10.v20150310.jar [new file with mode: 0644]
j8lib/jetty-io-9.2.10.v20150310.jar [new file with mode: 0644]
j8lib/jetty-server-9.2.10.v20150310.jar [new file with mode: 0644]
j8lib/jetty-util-9.2.10.v20150310.jar [new file with mode: 0644]
j8lib/jfreesvg-2.1.jar [new file with mode: 0644]
j8lib/jhall.jar [new file with mode: 0755]
j8lib/json_simple-1.1.jar [new file with mode: 0644]
j8lib/jsoup-1.8.1.jar [new file with mode: 0644]
j8lib/jsr311-api-1.1.1.jar [new file with mode: 0644]
j8lib/jswingreader-0.3.jar [new file with mode: 0644]
j8lib/libquaqua-8.0.jnilib.jar [new file with mode: 0644]
j8lib/libquaqua64-8.0.jnilib.jar [new file with mode: 0644]
j8lib/log4j-to-slf4j-2.0-rc2.jar [new file with mode: 0644]
j8lib/mail.jar [moved from lib/mail.jar with 100% similarity]
j8lib/miglayout-4.0-swing.jar [new file with mode: 0644]
j8lib/quaqua-filechooser-only-8.0.jar [new file with mode: 0644]
j8lib/regex.jar [new file with mode: 0755]
j8lib/saaj.jar [moved from lib/saaj.jar with 100% similarity]
j8lib/servlet-api-3.1.jar [moved from lib/servlet-api-3.1.jar with 100% similarity]
j8lib/slf4j-api-1.7.7.jar [moved from lib/slf4j-api-1.7.7.jar with 100% similarity]
j8lib/slf4j-log4j12-1.7.7.jar [moved from lib/slf4j-log4j12-1.7.7.jar with 100% similarity]
j8lib/vamsas-client.jar [new file with mode: 0644]
j8lib/wsdl4j.jar [moved from lib/wsdl4j.jar with 100% similarity]
j8lib/xercesImpl.jar [new file with mode: 0644]
j8lib/xml-apis.jar [moved from lib/xml-apis.jar with 100% similarity]
lib/.cvsignore [deleted file]
modules [new file with mode: 0644]
resources/images/Jalview_Logo_small_with_border.png [new file with mode: 0644]
resources/images/file.png [changed mode: 0755->0644]
resources/images/jalview_logo_background_fade-640x480.png [new file with mode: 0644]
resources/images/jalview_logos.icns [new file with mode: 0644]
resources/images/jalview_logos.ico [new file with mode: 0644]
resources/images/jetset_jalview_splash.gif [new file with mode: 0644]
resources/images/jetset_jalview_splash.png [new file with mode: 0644]
resources/images/link.gif [changed mode: 0755->0644]
resources/images/logo_big_v2.gif [changed mode: 0755->0644]
resources/images/logo_v2.gif [changed mode: 0755->0644]
resources/lang/Messages.properties
resources/lang/Messages_es.properties
schemas/jalview.xsd
schemas/vamsas.xsd
src/MCview/AppletPDBCanvas.java [new file with mode: 0644]
src/MCview/Atom.java [new file with mode: 0755]
src/MCview/PDBCanvas.java [new file with mode: 0644]
src/com/stevesoft/pat/RegOpt.java
src/com/stevesoft/pat/Regex.java
src/com/stevesoft/pat/RegexReader.java
src/com/stevesoft/pat/RegexTokenizer.java
src/ext/edu/ucsf/rbvi/strucviz2/ChimUtils.java
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraChain.java
src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java
src/jalview/analysis/AAFrequency.java
src/jalview/analysis/AlignmentSorter.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/Conservation.java
src/jalview/analysis/ParseProperties.java
src/jalview/analysis/SeqsetUtils.java
src/jalview/analysis/StructureFrequency.java
src/jalview/analysis/scoremodels/FeatureDistanceModel.java
src/jalview/api/AlignViewportI.java
src/jalview/api/FeatureColourI.java
src/jalview/appletgui/AlignFrame.java
src/jalview/appletgui/AlignViewport.java
src/jalview/appletgui/AnnotationColourChooser.java
src/jalview/appletgui/AnnotationLabels.java
src/jalview/appletgui/AnnotationPanel.java
src/jalview/appletgui/EmbmenuFrame.java
src/jalview/appletgui/FeatureColourChooser.java
src/jalview/appletgui/FeatureSettings.java
src/jalview/appletgui/FontChooser.java
src/jalview/appletgui/IdPanel.java
src/jalview/appletgui/OverviewPanel.java
src/jalview/appletgui/ScalePanel.java
src/jalview/appletgui/SeqPanel.java
src/jalview/appletgui/TreePanel.java
src/jalview/bin/BuildDetails.java
src/jalview/bin/Cache.java
src/jalview/bin/Jalview.java
src/jalview/bin/JalviewLite.java
src/jalview/bin/JalviewTaskbar.java [new file with mode: 0644]
src/jalview/bin/Launcher.java [new file with mode: 0644]
src/jalview/bin/MemorySetting.java [new file with mode: 0644]
src/jalview/commands/EditCommand.java
src/jalview/controller/AlignViewController.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/AlignmentAnnotation.java
src/jalview/datamodel/BinarySequence.java
src/jalview/datamodel/ColumnSelection.java
src/jalview/datamodel/DBRefEntry.java
src/jalview/datamodel/GeneLociI.java
src/jalview/datamodel/GeneLocus.java [new file with mode: 0644]
src/jalview/datamodel/Sequence.java
src/jalview/ext/ensembl/EnsemblFeatures.java
src/jalview/ext/ensembl/EnsemblGene.java
src/jalview/ext/ensembl/EnsemblLookup.java
src/jalview/ext/ensembl/EnsemblMap.java
src/jalview/ext/htsjdk/VCFReader.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/paradise/Annotate3D.java
src/jalview/fts/core/FTSRestClient.java
src/jalview/fts/service/pdb/PDBFTSRestClient.java
src/jalview/gui/APQHandlers.java [new file with mode: 0644]
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AppJmol.java
src/jalview/gui/BlogReader.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/CrossRefAction.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureRenderer.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/FeatureTypeSettings.java
src/jalview/gui/JvSwingUtils.java
src/jalview/gui/PCAPanel.java
src/jalview/gui/Preferences.java
src/jalview/gui/RotatableCanvas.java
src/jalview/gui/SequenceRenderer.java
src/jalview/gui/SplitFrame.java
src/jalview/gui/StructureChooser.java
src/jalview/gui/VamsasApplication.java
src/jalview/gui/WebserviceInfo.java
src/jalview/gui/WsPreferences.java
src/jalview/io/AnnotationFile.java
src/jalview/io/BackupFilenameParts.java
src/jalview/io/BackupFiles.java
src/jalview/io/BackupFilesPresetEntry.java [new file with mode: 0644]
src/jalview/io/FeaturesFile.java
src/jalview/io/FileLoader.java
src/jalview/io/IntKeyStringValueEntry.java [new file with mode: 0644]
src/jalview/io/JPredFile.java
src/jalview/io/JnetAnnotationMaker.java
src/jalview/io/ModellerDescription.java
src/jalview/io/NewickFile.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/io/VamsasAppDatastore.java
src/jalview/io/gff/SequenceOntologyLite.java
src/jalview/io/vamsas/DatastoreRegistry.java
src/jalview/io/vamsas/Rangetype.java
src/jalview/io/vamsas/Sequencefeature.java
src/jalview/io/vamsas/Tree.java
src/jalview/io/vcf/VCFLoader.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GCutAndPasteHtmlTransfer.java
src/jalview/jbgui/GCutAndPasteTransfer.java
src/jalview/jbgui/GDesktop.java
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GWebserviceInfo.java
src/jalview/project/Jalview2XML.java
src/jalview/schemes/ColourSchemes.java
src/jalview/schemes/Consensus.java
src/jalview/schemes/FeatureColour.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/util/ImageMaker.java
src/jalview/util/Platform.java
src/jalview/util/ShortcutKeyMaskExWrapper.java [new file with mode: 0644]
src/jalview/util/ShortcutKeyMaskExWrapper11.java [new file with mode: 0644]
src/jalview/util/ShortcutKeyMaskExWrapper8.java [new file with mode: 0644]
src/jalview/util/ShortcutKeyMaskExWrapperI.java [new file with mode: 0644]
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/ws/DBRefFetcher.java
src/jalview/ws/SequenceFetcher.java
src/jalview/ws/jws2/RNAalifoldClient.java
src/jalview/ws/jws2/jabaws2/Jws2Instance.java
src/jalview/ws/rest/HttpResultSet.java
src/jalview/xml/binding/jalview/AlcodonFrame.java
src/jalview/xml/binding/jalview/Annotation.java
src/jalview/xml/binding/jalview/AnnotationColourScheme.java
src/jalview/xml/binding/jalview/AnnotationElement.java
src/jalview/xml/binding/jalview/DoubleMatrix.java
src/jalview/xml/binding/jalview/DoubleVector.java
src/jalview/xml/binding/jalview/Feature.java
src/jalview/xml/binding/jalview/FeatureMatcher.java
src/jalview/xml/binding/jalview/FeatureMatcherSet.java
src/jalview/xml/binding/jalview/FilterBy.java
src/jalview/xml/binding/jalview/JalviewModel.java
src/jalview/xml/binding/jalview/JalviewModelType.java [new file with mode: 0644]
src/jalview/xml/binding/jalview/JalviewUserColours.java
src/jalview/xml/binding/jalview/MapListType.java
src/jalview/xml/binding/jalview/Mapping.java
src/jalview/xml/binding/jalview/NoValueColour.java
src/jalview/xml/binding/jalview/ObjectFactory.java
src/jalview/xml/binding/jalview/PcaDataType.java
src/jalview/xml/binding/jalview/Pdbentry.java
src/jalview/xml/binding/jalview/Sequence.java
src/jalview/xml/binding/jalview/SequenceSet.java
src/jalview/xml/binding/jalview/SequenceType.java
src/jalview/xml/binding/jalview/ThresholdType.java
src/jalview/xml/binding/jalview/VAMSAS.java
src/jalview/xml/binding/jalview/WebServiceParameterSet.java
src/jalview/xml/binding/jalview/package-info.java
src/org/jibble/epsgraphics/EpsGraphics2D.java
src/uk/ac/ebi/www/InputParams.java
src/uk/ac/ebi/www/picr/AccessionMappingService/AccessionMapperBindingStub.java
test/jalview/analysis/AlignmentUtilsTests.java
test/jalview/bin/CommandLineOperations.java
test/jalview/commands/EditCommandTest.java
test/jalview/datamodel/ColumnSelectionTest.java
test/jalview/datamodel/SequenceFeatureTest.java
test/jalview/gui/AlignFrameTest.java
test/jalview/gui/SeqPanelTest.java
test/jalview/io/BackupFilesTest.java
test/jalview/io/IdentifyFileTest.java
test/jalview/io/StockholmFileTest.java
test/jalview/io/backupfilestest.fa [moved from examples/backupfilestest.fa with 100% similarity]
test/jalview/io/testProps.jvprops
test/jalview/io/vcf/VCFLoaderTest.java
test/jalview/project/Jalview2xmlTests.java
test/jalview/schemes/FeatureColourTest.java
test/jalview/util/PlatformTest.java
test/jalview/ws/SequenceFetcherTest.java
utils/HelpLinksChecker.java
utils/archive_building.sh [new file with mode: 0644]
utils/de-multi-release-jar.sh [new file with mode: 0755]
utils/deprecation_auto_fixes.sh [new file with mode: 0644]
utils/dev_macos_install.sh [new file with mode: 0755]
utils/getJavaVersion.java
utils/getdown/jalview_logo_background_fade-640x480.xcf [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown-640x480.png [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown-progress-TEST2.png [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown-progress.png [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown-progress1.png [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown-progress2.png [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown-progress3-0.png [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown-progress3-1.png [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown-progress3.xcf [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown-progress4.png [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown_error-640x480.png [new file with mode: 0644]
utils/getdown/jalview_logo_background_getdown_instant-640x480.png [new file with mode: 0644]
utils/install4j/DS_Store [new file with mode: 0644]
utils/install4j/DS_Store_1 [new file with mode: 0644]
utils/install4j/DS_Store_2 [new file with mode: 0644]
utils/install4j/DS_Store_3 [new file with mode: 0644]
utils/install4j/Info_plist_file_associations.xml [new file with mode: 0644]
utils/install4j/Jalview-File.icns [new file with mode: 0644]
utils/install4j/Jalview-File.ico [new file with mode: 0644]
utils/install4j/Jalview-Version-Locator.icns [new file with mode: 0644]
utils/install4j/Jalview-Version-Locator.ico [new file with mode: 0644]
utils/install4j/Jalview-Version-Locator.png [new file with mode: 0644]
utils/install4j/JalviewVersionLocator.png [new file with mode: 0644]
utils/install4j/Uninstall Old Jalview.app/Contents/Info.plist [new file with mode: 0644]
utils/install4j/Uninstall Old Jalview.app/Contents/MacOS/applet [new file with mode: 0755]
utils/install4j/Uninstall Old Jalview.app/Contents/PkgInfo [new file with mode: 0644]
utils/install4j/Uninstall Old Jalview.app/Contents/Resources/Scripts/main.scpt [new file with mode: 0644]
utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.icns [new file with mode: 0644]
utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.rsrc [new file with mode: 0644]
utils/install4j/Uninstall Old Jalview.app/Contents/Resources/description.rtfd/TXT.rtf [new file with mode: 0644]
utils/install4j/Uninstall Old Jalview.app/Contents/_CodeSignature/CodeResources [new file with mode: 0644]
utils/install4j/Uninstall Old Jalview.scpt [new file with mode: 0644]
utils/install4j/auto_file_associations.pl [new file with mode: 0755]
utils/install4j/bs_install4j_template.install4j [new file with mode: 0644]
utils/install4j/file_associations_auto-Info_plist.xml [new file with mode: 0644]
utils/install4j/file_associations_auto-install4j.xml [new file with mode: 0644]
utils/install4j/file_associations_template-Info_plist.xml [new file with mode: 0644]
utils/install4j/file_associations_template-install4j.xml [new file with mode: 0644]
utils/install4j/install4j_template.install4j [new file with mode: 0644]
utils/install4j/jalview_dmg_background.png [new file with mode: 0644]
utils/install4j/jalview_getdown.install4j [new file with mode: 0644]
utils/install4j/uninstall_app_dmg_file_inclusions.sh [new file with mode: 0644]
utils/install4j/uninstall_old_jalview.icns [new file with mode: 0644]
utils/install4j/uninstall_old_jalview_files.xml [new file with mode: 0644]
utils/jdeps_jlink_all.sh [new file with mode: 0755]
utils/modulify-jar.sh [new file with mode: 0755]
utils/publishHelp.xml [new file with mode: 0644]
utils/showJVMVersion.java [new file with mode: 0644]

index 004d432..0a7e4b3 100644 (file)
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-       <classpathentry kind="src" path="src"/>
-       <classpathentry kind="src" path="src2"/>
-       <classpathentry kind="src" path="test"/>
-       <classpathentry kind="src" path="utils"/>
-       <classpathentry kind="lib" path="lib/activation.jar"/>
-       <classpathentry kind="lib" path="lib/axis.jar" sourcepath="D:/axis-1_2RC2-src/axis-1_2RC2"/>
-       <classpathentry kind="lib" path="lib/commons-discovery.jar"/>
-       <classpathentry kind="lib" path="lib/jaxrpc.jar"/>
-       <classpathentry kind="lib" path="lib/jhall.jar"/>
-       <classpathentry kind="lib" path="lib/mail.jar"/>
-       <classpathentry kind="lib" path="lib/regex.jar"/>
-       <classpathentry kind="lib" path="lib/saaj.jar"/>
-       <classpathentry kind="lib" path="lib/wsdl4j.jar"/>
-       <classpathentry kind="lib" path="lib/xercesImpl.jar"/>
-       <classpathentry kind="lib" path="lib/JGoogleAnalytics_0.3.jar" sourcepath="/JGoogleAnalytics/src/main/java"/>
-       <classpathentry kind="lib" path="lib/vamsas-client.jar"/>
-       <classpathentry kind="lib" path="lib/commons-logging-1.1.1.jar"/>
-       <classpathentry kind="lib" path="lib/apache-mime4j-0.6.jar" sourcepath="G:/InstallsDir/Sources for Development/apache-mime4j-0.6-src.zip"/>
-       <classpathentry kind="lib" path="lib/httpclient-4.0.3.jar">
-               <attributes>
-                       <attribute name="javadoc_location" value="file:/D:/InstallsDir/Sources for Development/httpconnect/httpcomponents-client-4.0.3/javadoc/"/>
-               </attributes>
-       </classpathentry>
-       <classpathentry kind="lib" path="lib/httpcore-4.0.1.jar">
-               <attributes>
-                       <attribute name="javadoc_location" value="file:/D:/InstallsDir/Sources for Development/httpconnect/httpcomponents-client-4.0.3/javadoc/"/>
-               </attributes>
-       </classpathentry>
-       <classpathentry kind="lib" path="lib/httpmime-4.0.3.jar">
-               <attributes>
-                       <attribute name="javadoc_location" value="file:/D:/InstallsDir/Sources for Development/httpconnect/httpcomponents-client-4.0.3/javadoc/"/>
-               </attributes>
-       </classpathentry>
-       <classpathentry kind="lib" path="lib/miglayout-4.0-swing.jar"/>
-       <classpathentry kind="lib" path="lib/jswingreader-0.3.jar" sourcepath="/jswingreader"/>
-       <classpathentry kind="lib" path="lib/commons-codec-1.3.jar"/>
-       <classpathentry kind="lib" path="lib/jabaws-min-client-2.2.0.jar" sourcepath="/clustengine"/>
-       <classpathentry kind="lib" path="lib/json_simple-1.1.jar" sourcepath="/Users/jimp/Downloads/json_simple-1.1-all.zip"/>
-       <classpathentry kind="lib" path="lib/slf4j-api-1.7.7.jar"/>
-       <classpathentry kind="lib" path="lib/jsoup-1.8.1.jar"/>
-       <classpathentry kind="lib" path="lib/log4j-to-slf4j-2.0-rc2.jar"/>
-       <classpathentry kind="lib" path="lib/slf4j-log4j12-1.7.7.jar"/>
-       <classpathentry kind="lib" path="lib/VARNAv3-93.jar"/>
-       <classpathentry kind="lib" path="lib/jfreesvg-2.1.jar"/>
-       <classpathentry kind="lib" path="lib/quaqua-filechooser-only-8.0.jar"/>
-       <classpathentry kind="lib" path="lib/VAqua5-patch.jar"/>
-       <classpathentry kind="lib" path="utils/classgraph-4.1.6.jar"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/plugin"/>
-       <classpathentry kind="lib" path="lib/xml-apis.jar"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/Plugin.jar"/>
-       <classpathentry kind="lib" path="lib/jersey-client-1.19.jar"/>
-       <classpathentry kind="lib" path="lib/jersey-core-1.19.jar"/>
-       <classpathentry kind="lib" path="lib/jsr311-api-1.1.1.jar"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/plugin.jar"/>
-       <classpathentry kind="lib" path="lib/jetty-server-9.2.10.v20150310.jar"/>
-       <classpathentry kind="lib" path="lib/servlet-api-3.1.jar"/>
-       <classpathentry kind="lib" path="lib/jetty-util-9.2.10.v20150310.jar"/>
-       <classpathentry kind="lib" path="lib/jetty-http-9.2.10.v20150310.jar"/>
-       <classpathentry kind="lib" path="lib/jetty-io-9.2.10.v20150310.jar"/>
-       <classpathentry kind="lib" path="lib/java-json.jar"/>
-       <classpathentry kind="con" path="org.testng.TESTNG_CONTAINER"/>
-       <classpathentry kind="lib" path="lib/biojava-core-4.1.0.jar"/>
-       <classpathentry kind="lib" path="lib/biojava-ontology-4.1.0.jar"/>
-       <classpathentry kind="lib" path="lib/htsjdk-2.12.0.jar"/>
-       <classpathentry kind="lib" path="lib/groovy-all-2.4.12-indy.jar"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-       <classpathentry exported="true" kind="con" path="GROOVY_DSL_SUPPORT"/>
-       <classpathentry kind="lib" path="lib/Jmol-14.29.17.jar"/>
-       <classpathentry kind="lib" path="lib/intervalstore-v1.0.jar"/>
-       <classpathentry kind="output" path="classes"/>
+       <classpathentry kind="src" output="bin/main" path="src">
+               <attributes>
+                       <attribute name="gradle_scope" value="main"/>
+                       <attribute name="gradle_used_by_scope" value=""/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="src" output="bin/test" path="test">
+               <attributes>
+                       <attribute name="test" value="true"/>
+                       <attribute name="gradle_scope" value="test"/>
+                       <attribute name="gradle_used_by_scope" value=""/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
+               <attributes>
+                       <attribute name="module" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
+       <classpathentry kind="lib" path="help"/>
+       <classpathentry kind="lib" path="resources"/>
+       <classpathentry kind="lib" path="j11lib/apache-mime4j-0.6.jar"/>
+       <classpathentry kind="lib" path="j11lib/axis.jar"/>
+       <classpathentry kind="lib" path="j11lib/biojava-core-4.1.0.jar"/>
+       <classpathentry kind="lib" path="j11lib/biojava-ontology-4.1.0.jar"/>
+       <classpathentry kind="lib" path="j11lib/commons-codec-1.3.jar"/>
+       <classpathentry kind="lib" path="j11lib/commons-compress-1.18.jar"/>
+       <classpathentry kind="lib" path="j11lib/commons-discovery.jar"/>
+       <classpathentry kind="lib" path="j11lib/commons-logging-1.1.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/FastInfoset.jar"/>
+       <classpathentry kind="lib" path="j11lib/getdown-core.jar"/>
+       <classpathentry kind="lib" path="j11lib/gmbal-api-only-MODULE.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-ant-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-bsf-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-cli-commons-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-cli-picocli-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-console-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-datetime-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-dateutil-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-docgenerator-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-groovydoc-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-groovysh-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-jaxb-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-jmx-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-json-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-json-direct-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-jsr223-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-macro-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-nio-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-servlet-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-sql-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-swing-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-templates-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-test-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-test-junit5-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-testng-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/groovy-xml-2.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/htsjdk-2.12.0.jar"/>
+       <classpathentry kind="lib" path="j11lib/httpclient-4.0.3.jar"/>
+       <classpathentry kind="lib" path="j11lib/httpcore-4.0.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/httpmime-4.0.3.jar"/>
+       <classpathentry kind="lib" path="j11lib/intervalstore-v1.0.jar"/>
+       <classpathentry kind="lib" path="j11lib/istack-commons-runtime.jar"/>
+       <classpathentry kind="lib" path="j11lib/jabaws-min-client-2.2.0.jar"/>
+       <classpathentry kind="lib" path="j11lib/java-json.jar"/>
+       <classpathentry kind="lib" path="j11lib/javax.activation-api-1.2.0.jar"/>
+       <classpathentry kind="lib" path="j11lib/javax.annotation-api-1.3.2.jar"/>
+       <classpathentry kind="lib" path="j11lib/javax.jws-api-1.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/javax.servlet-api-4.0.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/javax.xml.rpc-api-1.1.2.jar"/>
+       <classpathentry kind="lib" path="j11lib/javax.xml.soap-api-1.4.0.jar"/>
+       <classpathentry kind="lib" path="j11lib/jaxb-api-2.3.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/jaxb-runtime-2.3.2.jar"/>
+       <classpathentry kind="lib" path="j11lib/jaxws-api-2.3.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/jaxws-rt-java9.jar"/>
+       <classpathentry kind="lib" path="j11lib/jersey-client-1.19.4.jar"/>
+       <classpathentry kind="lib" path="j11lib/jersey-core-1.19.4.jar"/>
+       <classpathentry kind="lib" path="j11lib/jersey-json-1.19.4.jar"/>
+       <classpathentry kind="lib" path="j11lib/jetty-http-9.2.10.v20150310.jar"/>
+       <classpathentry kind="lib" path="j11lib/jetty-io-9.2.10.v20150310.jar"/>
+       <classpathentry kind="lib" path="j11lib/jetty-server-9.2.10.v20150310.jar"/>
+       <classpathentry kind="lib" path="j11lib/jetty-util-9.2.10.v20150310.jar"/>
+       <classpathentry kind="lib" path="j11lib/jfreesvg-2.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/JGoogleAnalytics_0.3.jar"/>
+       <classpathentry kind="lib" path="j11lib/jhall.jar"/>
+       <classpathentry kind="lib" path="j11lib/Jmol-14.29.17.jar"/>
+       <classpathentry kind="lib" path="j11lib/json_simple-1.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/jsoup-1.8.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/jsr311-api-1.1.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/jswingreader-0.3.jar"/>
+       <classpathentry kind="lib" path="j11lib/libquaqua-8.0.jnilib.jar"/>
+       <classpathentry kind="lib" path="j11lib/libquaqua64-8.0.jnilib.jar"/>
+       <classpathentry kind="lib" path="j11lib/log4j-to-slf4j-2.0-rc2.jar"/>
+       <classpathentry kind="lib" path="j11lib/mail-MODULE.jar"/>
+       <classpathentry kind="lib" path="j11lib/miglayout-4.0-swing.jar"/>
+       <classpathentry kind="lib" path="j11lib/mimepull-1.9.11.jar"/>
+       <classpathentry kind="lib" path="j11lib/policy-2.7.6.jar"/>
+       <classpathentry kind="lib" path="j11lib/quaqua-filechooser-only-8.0.jar"/>
+       <classpathentry kind="lib" path="j11lib/regex.jar"/>
+       <classpathentry kind="lib" path="j11lib/saaj-impl.jar"/>
+       <classpathentry kind="lib" path="j11lib/slf4j-api-1.7.26.jar"/>
+       <classpathentry kind="lib" path="j11lib/slf4j-log4j12-1.7.26.jar"/>
+       <classpathentry kind="lib" path="j11lib/stax-ex-1.8.1.jar"/>
+       <classpathentry kind="lib" path="j11lib/stax2-api-4.2.jar"/>
+       <classpathentry kind="lib" path="j11lib/streambuffer-1.5.7.jar"/>
+       <classpathentry kind="lib" path="j11lib/txw2-2.3.2.jar"/>
+       <classpathentry kind="lib" path="j11lib/vamsas-client.jar"/>
+       <classpathentry kind="lib" path="j11lib/VAqua5-patch.jar"/>
+       <classpathentry kind="lib" path="j11lib/VARNAv3-93.jar"/>
+       <classpathentry kind="lib" path="j11lib/wsdl4j-1.6.3.jar"/>
+       <classpathentry kind="lib" path="j11lib/xercesImpl.jar"/>
+       <classpathentry kind="lib" path="utils/ant-contrib-1.0b3.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/axis-ant.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/classgraph-4.1.6.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/hamcrest-core-1.3.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/jhall.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/jhindexer.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/junit-4.12.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/proguard_5.3.3.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/roxes-ant-tasks-1.2-2004-01-30.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/testnglibs/bsh-2.0b4.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/testnglibs/guava-base-r03.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/testnglibs/guava-collections-r03.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/testnglibs/jcommander.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/testnglibs/junit-4.12.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/testnglibs/snakeyaml.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/testnglibs/testng-sources.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/testnglibs/testng.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="lib" path="utils/wsdl4j.jar">
+               <attributes>
+                       <attribute name="test" value="true"/>
+               </attributes>
+       </classpathentry>
+       <classpathentry kind="output" path="bin/main"/>
 </classpath>
index 6c40472..d4d0c20 100644 (file)
--- a/.project
+++ b/.project
@@ -1,37 +1,23 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <projectDescription>
-       <name>Jalview Release 2.7-old</name>
+       <name>Jalview with gradle build</name>
        <comment></comment>
-       <projects>
-       </projects>
+       <projects/>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
+               <nature>org.eclipse.buildship.core.gradleprojectnature</nature>
+       </natures>
        <buildSpec>
                <buildCommand>
                        <name>org.eclipse.jdt.core.javabuilder</name>
-                       <arguments>
-                       </arguments>
-               </buildCommand>
-               <buildCommand>
-                       <name>org.eclipse.wst.common.project.facet.core.builder</name>
-                       <arguments>
-                       </arguments>
+                       <arguments/>
                </buildCommand>
                <buildCommand>
-                       <name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
-                       <triggers>full,incremental,</triggers>
-                       <arguments>
-                               <dictionary>
-                                       <key>LaunchConfigHandle</key>
-                                       <value>&lt;project&gt;/.externalToolBuilders/Jalview Release indices [Builder].launch</value>
-                               </dictionary>
-                       </arguments>
+                       <name>org.eclipse.buildship.core.gradleprojectbuilder</name>
+                       <arguments/>
                </buildCommand>
        </buildSpec>
-       <natures>
-               <nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
-               <nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
-               <nature>org.eclipse.jdt.core.javanature</nature>
-               <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
-               <nature>de.tud.st.ispace.builder.ISpaceNature</nature>
-               <nature>org.eclipse.jem.beaninfo.BeanInfoNature</nature>
-       </natures>
+       <linkedResources/>
+       <filteredResources/>
 </projectDescription>
diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs
new file mode 100644 (file)
index 0000000..e889521
--- /dev/null
@@ -0,0 +1,2 @@
+connection.project.dir=
+eclipse.preferences.version=1
diff --git a/.settings/org.eclipse.jdt.core.jalview.prefs b/.settings/org.eclipse.jdt.core.jalview.prefs
new file mode 100644 (file)
index 0000000..4b7c545
--- /dev/null
@@ -0,0 +1,298 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=11
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=52
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=next_line_on_wrap
+org.eclipse.jdt.core.formatter.brace_position_for_block=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=false
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=4
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.indentation.size=8
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=76
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+
index 32ad05c..11ddce1 100644 (file)
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled
-org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
-org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
-org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
-org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary=
-org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
-org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
-org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=1.8
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
-org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
-org.eclipse.jdt.core.compiler.problem.deadCode=warning
-org.eclipse.jdt.core.compiler.problem.deprecation=warning
-org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
-org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
-org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
-org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
-org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
-org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
-org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
-org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
-org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled
-org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
-org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning
-org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
-org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
-org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
-org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
-org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
-org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
-org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
-org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning
-org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
-org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
-org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
-org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
-org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
-org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning
-org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
-org.eclipse.jdt.core.compiler.problem.nullReference=warning
-org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
-org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
-org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
-org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning
-org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
-org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
-org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
-org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
-org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
-org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
-org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
-org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
-org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
-org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
-org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
-org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
-org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning
-org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
-org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
-org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
-org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
-org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
-org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
-org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
-org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
-org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedImport=warning
-org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
-org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
-org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
-org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
-org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
-org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
-org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
-org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
-org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
-org.eclipse.jdt.core.compiler.release=disabled
-org.eclipse.jdt.core.compiler.source=1.8
-org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false
-org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
-org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
-org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false
-org.eclipse.jdt.core.formatter.align_with_spaces=false
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=52
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_assignment=0
-org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
-org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
-org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16
-org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
-org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
-org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
-org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
-org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
-org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
-org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
-org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
-org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
-org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
-org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
-org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+#
+#Tue Jul 16 08:09:58 BST 2019
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
 org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_after_package=1
-org.eclipse.jdt.core.formatter.blank_lines_before_field=1
-org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.indentation.size=8
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.continuation_indentation=4
 org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
-org.eclipse.jdt.core.formatter.blank_lines_before_method=1
-org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
-org.eclipse.jdt.core.formatter.blank_lines_before_package=0
-org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
 org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
-org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=next_line
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
 org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=next_line
-org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=next_line_on_wrap
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.use_on_off_tags=true
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
 org.eclipse.jdt.core.formatter.brace_position_for_block=next_line
-org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=next_line
 org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=next_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=next_line
-org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=next_line
 org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
-org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line
-org.eclipse.jdt.core.formatter.brace_position_for_switch=next_line
-org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line
-org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false
-org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
 org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
-org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false
-org.eclipse.jdt.core.formatter.comment.format_block_comments=false
-org.eclipse.jdt.core.formatter.comment.format_header=false
-org.eclipse.jdt.core.formatter.comment.format_html=true
-org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
-org.eclipse.jdt.core.formatter.comment.format_line_comments=true
-org.eclipse.jdt.core.formatter.comment.format_source_code=true
-org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
-org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
-org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
-org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
-org.eclipse.jdt.core.formatter.comment.line_length=80
-org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
-org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
-org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
-org.eclipse.jdt.core.formatter.compact_else_if=true
-org.eclipse.jdt.core.formatter.continuation_indentation=4
-org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
-org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
-org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
-org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
 org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
-org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
-org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_empty_lines=false
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
-org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
-org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
-org.eclipse.jdt.core.formatter.indentation.size=2
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
-org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
-org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
 org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
 org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
-org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
-org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
-org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_assignment=0
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=next_line
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.comment.format_header=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
 org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=next_line
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.compiler.source=11
 org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
+org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
 org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
+org.eclipse.jdt.core.formatter.blank_lines_before_field=1
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
+org.eclipse.jdt.core.formatter.brace_position_for_switch=next_line
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+eclipse.preferences.version=1
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
 org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=52
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=insert
 org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.comment.format_block_comments=false
+org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
 org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=next_line_on_wrap
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.compiler.compliance=11
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=next_line
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
 org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert
-org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
-org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
-org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
-org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
-org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
-org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
 org.eclipse.jdt.core.formatter.join_lines_in_comments=true
-org.eclipse.jdt.core.formatter.join_wrapped_lines=true
-org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=true
-org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
-org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false
-org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
-org.eclipse.jdt.core.formatter.lineSplit=76
-org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
-org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
-org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
-org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
-org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
-org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
-org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
-org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
-org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
-org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
-org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
-org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
-org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
-org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
 org.eclipse.jdt.core.formatter.tabulation.char=space
-org.eclipse.jdt.core.formatter.tabulation.size=2
-org.eclipse.jdt.core.formatter.use_on_off_tags=true
-org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
-org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
-org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
-org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
-org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
-org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
-org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.lineSplit=76
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
index 078b4d9..ed6a53d 100644 (file)
@@ -1,7 +1,7 @@
 eclipse.preferences.version=1
 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
 formatter_profile=_Jalview
-formatter_settings_version=14
+formatter_settings_version=16
 org.eclipse.jdt.ui.ignorelowercasenames=true
 org.eclipse.jdt.ui.importorder=jalview;java;javax;org;com;
 org.eclipse.jdt.ui.ondemandthreshold=99
diff --git a/JAVA-11-README b/JAVA-11-README
new file mode 100644 (file)
index 0000000..fabae31
--- /dev/null
@@ -0,0 +1,116 @@
+2019-07-01 (pre 2.11.0 release)
+Notes below are out of date, but might be useful for future development with Java 11
+
+Presently the Java 11 build of Jalview treats all libraries as libraries on the classpath, and does not use them as modules. The Java 11 JRE prepped for future release is a standard AdoptOpenJDK release (possibly repackaged for getdown/install4j to use).
+
+At time of writing, the JRE cannot be updated over the air on macOS platform due to a limitation of getdown's unpackaging system only using jars and hence cannot work with symbolic links.  There is a symbolic link that is essential (as a symbolic link) in the macOS distribution (Contents/MacOS/libjli.dylib -> ../Home/lib/jli/libjli.dylib).  Without this, the install4j launcher will not work.  Some work has started on incorporating tar capabilities in getdown using the apache-commons-compress library, though this is not complete.
+
+Future releases might incorporate Java 11 modules into a bespoke Java 11 JRE that can be updated over the air via getdown.This could reduce the size of the distribution but will absolutely need the bespoke JRE.  This would mean Jalview being restricted to platforms that we (can) support, or distributing the modules as libraries in a second distribution (the shadowJar distribution).
+
+build.gradle is written to support compilation of either Java 1.8 or Java 11 compatible bytecode.  Please note that the compilation (and therefore build) process REQUIRES a Java 11 (or above) JDK.  This is because there is Java 11 specific code in some Jalview classes (devolved into separate classes which fail "gracefully" when read by a Java 1.8 JRE).
+Java 11 is therefore the default build target, but can be changed simply by specifying the property JAVA_VERSION=1.8 (e.g.
+
+gradle compileJava -PJAVA_VERSION=1.8
+
+).  Some different versions of supporting libraries are used between Java 1.8 and 11 builds, and can be found in j8lib and j11lib folders respectively.  Note that there are a number of extra libraries used in the Java 11 distribution which are present in the Java 1.8 JRE but not distributed with the Java 11 JRE.
+
+Also see doc/building.md or doc/building.html
+
+
+
+
+Old notes:
+
+Java 11 module replacements
+
+As Java 11 no longer ships with Java EE libraries, which were standard in Java 8, and available (modularised, through the --add-modules CLI mechanism) in Java 9 and 10, third party replacement libraries for these libraries/modules have to be found.
+
+Ideally, these jar files would be packaged as modules (i.e. would contains a module-info.class with the relevant provision and requirements information for that module package).  However, as at time of writing (end Nov 2018) the available packages are not fully mature.
+There also seems to be no single place to look when trying to obtain modules jar files.
+
+A couple of good places to start:
+https://javaee.github.io/
+(see Metro, Glassfish etc)
+
+https://search.maven.org/
+(search for e.g. a:javax.activation)
+
+https://mvnrepository.com/
+(if the other two fail!)
+
+Unfortunately there seems to be multiple projects providing the same modules, with little indication of which is "best".
+Here's an example of what to do with search.maven.org, looking for, say, the java.activation module
+
+Firstly, remember this might be called javax.activation (in fact it is), so searching for
+
+a:javax.activation
+
+shows 3 possible candidates.
+
+The "Updated" date gives a clue if a library might be up-to-date or still being maintained, and of the three listed, com.sun.activation version 1.2.0 was updated in Sep 2017, and is the only one updated in the last 5 years, so it looks like a good candidate.
+Click on the arrow in the Download column which gives various download options.  Choose "jar" and this should download straight away.
+
+If you have a peek inside the jar file:
+
+$ jar -tvf ~/Downloads/javax.activation-1.2.0.jar
+
+     0 Wed Sep 06 16:13:08 BST 2017 META-INF/
+  1307 Wed Sep 06 16:13:06 BST 2017 META-INF/MANIFEST.MF
+     0 Wed Sep 06 14:23:50 BST 2017 javax/
+     0 Wed Sep 06 14:23:50 BST 2017 javax/activation/
+     0 Wed Sep 06 14:23:50 BST 2017 com/
+     0 Wed Sep 06 14:23:50 BST 2017 com/sun/
+     0 Wed Sep 06 14:23:50 BST 2017 com/sun/activation/
+...
+  2238 Wed Sep 06 14:23:50 BST 2017 com/sun/activation/registries/LineTokenizer.class
+ 39394 Wed Sep 06 16:13:06 BST 2017 META-INF/LICENSE.txt
+   581 Wed Sep 06 16:13:06 BST 2017 META-INF/mimetypes.default
+   292 Wed Sep 06 16:13:06 BST 2017 META-INF/mailcap.default
+     0 Wed Sep 06 16:13:08 BST 2017 META-INF/maven/
+     0 Wed Sep 06 16:13:08 BST 2017 META-INF/maven/com.sun.activation/
+     0 Wed Sep 06 16:13:08 BST 2017 META-INF/maven/com.sun.activation/javax.activation/
+  6515 Fri Sep 01 16:13:04 BST 2017 META-INF/maven/com.sun.activation/javax.activation/pom.xml
+   119 Wed Sep 06 14:23:52 BST 2017 META-INF/maven/com.sun.activation/javax.activation/pom.properties
+
+
+you can see that it doesn't have a module-info.class file, meaning it can only be used as an "automatic" module.  This will probably mean it can't be used with jlink when creating a JRE.
+HAVE NO FEAR!
+We can semi-manually (urgh!) create a module-info.class for this library and turn it into a module.  This really should be scripted. I might just do it.
+
+So, assuming we downloaded that jar to ~/Downloads/javax.activation-1.2.0.jar, and JAVA_HOME is set to a java distribution, we can do:
+
+mkdir tmp
+cd tmp
+jar -xvf ~/Downloads/javax.activation-1.2.0.jar
+jdeps --generate-module-info . ~/Downloads/javax.activation-1.2.0.jar
+# --generate-module-info creates the module-info.java file we are looking for! but for some reason insists on sticking it in a java.activation subdir (even though we ask for '.')
+mv java.activation/module-info.java .
+rmdir java.activation
+javac -d . module-info.java
+# we could clean up (rm) module-info.java, but I think it'll be more useful to keep a hold of it in the jar, it's not causing a problem or taking up space
+cd ..
+jar -cvf javax.activation-1.2.0-MODULE.jar -C ./tmp .
+/bin/rm -rf ./tmp
+
+and voila, you have a modulified version of the library in the jar file ./javax.activation-1.2.0-MODULE.jar.  This can be used with jlink.
+[ timeout: I scripted this as utils/modulify.sh  Usage: modulify.sh /path/to/jarfile.jar  ... creates /path/to/jarfile-MODULE.jar ]
+
+Once we have enough modulified jar files (note, we kind of keep downloading a new file or files until jalview starts without Exceptions, jdeps could probably provide a better way(!), and jaxws-rt provides quite a few of the needed jar files, hopefully in a coherent way).
+
+A list of module dependencies can be found with (note module-path doesn't need jar files, just the dirs to look in, unlike class-path)
+
+jdeps --class-path="lib/*:j11lib/*" --module-path="$JAVA_HOME/jmods:j11lib" --list-deps dist/jalview.jar libs/*.jar
+
+will end with a list of modules required in the JRE, these need to be comma-separated-listed for jlink.  We /ought/ to be able to do this by using "--print-module-deps" like this
+
+jdeps --class-path="lib/*:j11lib/*" --module-path="$JAVA_HOME/jmods:j11lib" --print-module-deps dist/jalview.jar libs/*.jar
+
+but that ends with an Exception.  Perhaps should look into that...
+
+Anyway, with the list of modules required (in the file "modules") you can do
+
+jlink --module-path $JAVA_HOME/jmods:j11lib --compress=2 --add-modules `cat modules` --no-header-files --no-man-pages --bind-services --output j11jre/openjdk11_platform
+
+To create a Java 11 JRE in j11jre/openjdk11_platform (or whatever you wish to call it).
+You can point JAVA_HOME at the JDK11 of a different platform, so long as the jlink in your path is to the jlink for the platform you're running on.
+
index 728711e..348827e 100644 (file)
@@ -13,53 +13,80 @@ ext.edu.ucsf.rbvi.strucviz2 includes sources originally developed by Scooter Mor
 
 Licensing information for each library is given below:
 
-JGoogleAnalytics_0.3.jar       APL 2.0 License - http://code.google.com/p/jgoogleanalytics/
-Jmol-14.6.4_2016.10.26.jar     GPL/LGPLv2 http://sourceforge.net/projects/jmol/files/
-VARNAv3-93.jar GPL licenced software by K�vin Darty, Alain Denise and Yann Ponty. http://varna.lri.fr
+j8lib:
+
 activation.jar 
 apache-mime4j-0.6.jar
 axis.jar
-castor-1.1-cycle-xml.jar
+biojava-core-4.1.0.jar LGPLv2.1 - latest license at https://github.com/biojava/biojava/blob/master/LICENSE
+biojava-ontology-4.1.0.jar     LGPLv2.1 - latest license at https://github.com/biojava/biojava/blob/master/LICENSE
 commons-codec-1.3.jar
-commons-discovery.jar
+commons-compress-1.18.jar
 commons-logging-1.1.1.jar
-commons-logging.jar
-commons-net-3.3.jar
-groovy-all-2.4.6-indy.jar      APL 2.0 License - downloaded and extracted from https://dl.bintray.com/groovy/maven/apache-groovy-binary-2.4.6.zip
+getdown-core.jar       Getdown license - https://github.com/threerings/getdown/blob/master/LICENSE
+groovy-all-2.4.12-indy.jar     APL 2.0 License - downloaded and extracted from https://dl.bintray.com/groovy/maven/apache-groovy-binary-2.4.6.zip
+htsjdk-2.12.0.jar      built from maven master at https://github.com/samtools/htsjdk MIT License to Broad Institute
 httpclient-4.0.3.jar
 httpcore-4.0.1.jar
 httpmime-4.0.3.jar
+intervalstore-v1.0.jar
+jabaws-min-client-2.2.0.jar
+java-json.jar
 jaxrpc.jar
+jersey-client-1.19.4.jar       CDDL 1.1 + GPL2 w/ CPE - http://glassfish.java.net/public/CDDL+GPL_1_1.html
+jersey-core-1.19.4.jar CDDL 1.1 + GPL2 w/ CPE - http://glassfish.java.net/public/CDDL+GPL_1_1.html
+jersey-json-1.19.4.jar CDDL 1.1 + GPL2 w/ CPE - http://glassfish.java.net/public/CDDL+GPL_1_1.html
+jetty-http-9.2.10.v20150310.jar
+jetty-io-9.2.10.v20150310.jar
+jetty-server-9.2.10.v20150310.jar
+jetty-util-9.2.10.v20150310.jar
+jfreesvg-2.1.jar       GPL v3 licensed library from the JFree suite - http://www.jfree.org/jfreesvg/
+JGoogleAnalytics_0.3.jar       APL 2.0 License - http://code.google.com/p/jgoogleanalytics/
 jhall.jar
-jswingreader-0.3.jar : Apache license - built from http://jswingreader.sourceforge.net/ svn/trunk v12
+Jmol-14.6.4_2016.10.26.jar     GPL/LGPLv2 http://sourceforge.net/projects/jmol/files/
+json_simple-1.1.jar    Apache 2.0 license - downloaded from https://code.google.com/p/json-simple/downloads/list (will move to 1.1.1 version when jalview is mavenised and osgi-ised)
+jsoup-1.8.1.jar
+jsr311-api-1.1.1.jar
+jswingreader-0.3.jar   Apache license - built from http://jswingreader.sourceforge.net/ svn/trunk v12
+libquaqua64-8.0.jnilib.jar     quaqua: v.8.0 (latest stable) by Randel S Hofer. LGPL and BSD Modified license: downloaded from http://www.randelshofer.ch/quaqua/ 
+libquaqua-8.0.jnilib.jar       quaqua: v.8.0 (latest stable) by Randel S Hofer. LGPL and BSD Modified license: downloaded from http://www.randelshofer.ch/quaqua/ 
 log4j-1.2.8.jar
 mail.jar
-miglayout-4.0-swing.jar        BSD http://www.migcalendar.com/miglayout/versions/4.0/license.txt
-min-jaba-client.jar
+miglayout-4.0-swing.jar        BSD - http://www.migcalendar.com/miglayout/versions/4.0/license.txt
+quaqua-filechooser-only-8.0.jar        quaqua: v.8.0 (latest stable) by Randel S Hofer. LGPL and BSD Modified license: downloaded from http://www.randelshofer.ch/quaqua/ 
 regex.jar
 saaj.jar
+servlet-api-3.1.jar
+slf4j-api-1.7.7.jar
+slf4j-log4j12-1.7.7.jar
 vamsas-client.jar
+VAqua5-patch.jar       This is a patched version of VAqua v5 (latest stable) by Alan Snyder et al. GPLv3 with Classpath exception, also includes contributions from Quaqua: http://violetlib.org/vaqua/overview.html - see doc/patching-vaqua.txt for patch details, and http://issues.jalview.org/browse/JAL-2988 for details of the bug that the patch addresses.
+VARNAv3-93.jar GPL licenced software by K�vin Darty, Alain Denise and Yann Ponty - http://varna.lri.fr
 wsdl4j.jar
 xercesImpl.jar
 xml-apis.jar
-json_simple-1.1.jar : Apache 2.0 license : downloaded from https://code.google.com/p/json-simple/downloads/list (will move to 1.1.1 version when jalview is mavenised and osgi-ised)
-jfreesvg-2.1.jar : GPL v3 licensed library from the JFree suite: http://www.jfree.org/jfreesvg/
-
-quaqua: v.8.0 (latest stable) by Randel S Hofer. LGPL and BSD Modified license: downloaded from http://www.randelshofer.ch/quaqua/ 
-
-vaqua5-patch: This is a patched version of VAqua v5 (latest stable) by Alan Snyder et al. GPLv3 with Classpath exception, also includes contributions from Quaqua: http://violetlib.org/vaqua/overview.html - see doc/patching-vaqua.txt for patch details, and http://issues.jalview.org/browse/JAL-2988 for details of the bug that the patch addresses.
-
-lib/htsjdk-2.12.jar: built from maven master at https://github.com/samtools/htsjdk MIT License to Broad Institute
-
-lib/biojava-core-4.1.0.jar LGPLv2.1 - latest license at https://github.com/biojava/biojava/blob/master/LICENSE
-
-lib/biojava-ontology-4.1.0.jar LGPLv2.1 - latest license at https://github.com/biojava/biojava/blob/master/LICENSE
 
 
 Libraries for Test Suite
 
-utils/classgraph-4.1.6.jar: BSD License - allows recovery of classpath for programmatic construction of a Java command line to launch Jalview
-                      version 4.1.6 downloaded from https://mvnrepository.com/artifact/io.github.classgraph/classgraph/4.1.6
+utils/axis-ant.jar
+utils/ant-contrib-1.0b3.jar
+utils/hamcrest-core-1.3.jar
+utils/roxes-ant-tasks-1.2-2004-01-30.jar
+utils/proguard_5.3.3.jar
+utils/jhindexer.jar
+utils/junit-4.12.jar
+utils/jhall.jar
+utils/classgraph-4.1.6.jar     BSD License - allows recovery of classpath for programmatic construction of a Java command line to launch Jalview version 4.1.6 downloaded from https://mvnrepository.com/artifact/io.github.classgraph/classgraph/4.1.6
+utils/testnglibs/guava-base-r03.jar
+utils/testnglibs/guava-collections-r03.jar
+utils/testnglibs/bsh-2.0b4.jar
+utils/testnglibs/snakeyaml.jar
+utils/testnglibs/testng-sources.jar
+utils/testnglibs/testng.jar
+utils/testnglibs/junit-4.12.jar
+utils/testnglibs/jcommander.jar
+utils/wsdl4j.jar
 
 
 Additional dependencies
@@ -69,3 +96,31 @@ examples/javascript/jquery*.js : BSD license
 examples/javascript/jshashtable-2.1.js : Apache License
 examples/javascript/facebox-1.3.js : MTI License - http://www.opensource.org/licenses/mit-license.php
 
+
+New in j11lib:
+
+FastInfoset.jar        CDDL Version 1.1 - https://javaee.github.io/metro-fi/LICENSE
+getdown-core.jar       Getdown license - https://github.com/threerings/getdown/blob/master/LICENSE
+groovy-*2.5.7.jar      Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.html
+istack-commons-runtime.jar     CDDL Version 1.1 - https://glassfish.java.net/public/CDDL+GPL_1_1.html
+javax.activation-api-1.2.0.jar CDDL/GPLv2+CE - https://github.com/javaee/activation/blob/master/LICENSE.txt
+javax.annotation-api-1.3.2.jar CDDL + GPLv2 with classpath exception - https://github.com/javaee/javax.annotation/blob/master/LICENSE
+javax.jws-api-1.1.jar  CDDL + GPLv2 with classpath exception - https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html
+javax.servlet-api-4.0.1.jar    CDDL + GPLv2 with classpath exception - https://oss.oracle.com/licenses/CDDL+GPL-1.1
+javax.xml.rpc-api-1.1.2.jar    CDDL + GPLv2 with classpath exception - https://oss.oracle.com/licenses/CDDL+GPL-1.1
+javax.xml.soap-api.jar CDDL + GPLv2 with classpath exception - https://github.com/javaee/javax.xml.soap/blob/master/LICENSE
+jaxb-api-2.3.1.jar     CDDL 1.1 + GPL2 w/ CPE - https://oss.oracle.com/licenses/CDDL+GPL-1.1
+jaxb-runtime-2.3.2.jar Eclipse Distribution License - v 1.0 - http://www.eclipse.org/org/documents/edl-v10.php
+jaxws-api-2.3.1.jar    CDDL + GPLv2 with classpath exception - https://github.com/javaee/jax-ws-spec/blob/master/LICENSE.md
+Jmol-14.6.4_2016.10.26-no_netscape.jar GPL/LGPLv2 http://sourceforge.net/projects/jmol/files/
+jsr311-api-1.1.1.jar   CDDL License - http://www.opensource.org/licenses/cddl1.php
+mimepull-1.9.11.jar    Eclipse Distribution License - v 1.0 - http://www.eclipse.org/org/documents/edl-v10.php
+policy-2.7.6.jar       Eclipse Distribution License - v 1.0 - http://www.eclipse.org/org/documents/edl-v10.php
+slf4j-api-1.7.26.jar   MIT License - https://opensource.org/licenses/mit-license.php
+slf4j-log4j12-1.7.26.jar       MIT License - https://opensource.org/licenses/mit-license.php
+stax-ex-1.8.1.jar      Eclipse Distribution License - v 1.0 - http://www.eclipse.org/org/documents/edl-v10.php
+stax2-api-4.2.jar      The BSD License - http://www.opensource.org/licenses/bsd-license.php
+streambuffer-1.5.7.jar Eclipse Distribution License - v 1.0 - http://www.eclipse.org/org/documents/edl-v10.php
+txw2-2.3.2.jar Eclipse Distribution License - v 1.0 - http://www.eclipse.org/org/documents/edl-v10.php
+wsdl4j-1.6.3.jar       CPL - http://www.opensource.org/licenses/cpl1.0.txt
+
diff --git a/authors.props b/authors.props
new file mode 100644 (file)
index 0000000..3c06708
--- /dev/null
@@ -0,0 +1,4 @@
+YEAR=2018
+AUTHORS=J Procter, M Carstairs, B Soares, K Mourao, TC Ofoegbu, AM Waterhouse, J Engelhardt, LM Lui, A Menard, D Barton, N Sherstnev, D Roldan-Martinez, M Clamp, S Searle, G Barton
+AUTHORFNAMES=Jim Procter, Mungo Carstairs, Ben Soares, Kira Mourao, Tochukwu 'Charles' Ofoegbu, Andrew Waterhouse, Jan Engelhardt, Lauren Lui, Anne Menard, Daniel Barton, Natasha Sherstnev, David Roldan-Martinez, Michele Clamp, James Cuff, Steve Searle, David Martin & Geoff Barton
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644 (file)
index 0000000..f02d8ad
--- /dev/null
@@ -0,0 +1,1249 @@
+import org.apache.tools.ant.filters.ReplaceTokens
+//import org.apache.tools.ant.filters.ReplaceRegexp
+import org.gradle.internal.os.OperatingSystem
+import org.gradle.plugins.ide.eclipse.model.*
+
+
+import groovy.transform.ExternalizeMethods
+
+buildscript {
+  dependencies {
+    classpath 'org.openclover:clover:4.3.1'
+    classpath 'org.apache.commons:commons-compress:1.18'
+  }
+}
+
+plugins {
+  id 'java'
+  id 'application'
+  id 'eclipse'
+  id 'com.github.johnrengelman.shadow' version '4.0.3'
+  id 'com.install4j.gradle' version '7.0.9'
+}
+
+repositories {
+  jcenter()
+  mavenCentral()
+  mavenLocal()
+  flatDir {
+    dirs gradlePluginsDir
+  }
+}
+
+mainClassName = launcherClass
+def cloverInstrDir = file("$buildDir/$cloverSourcesInstrDir")
+def classes = "$jalviewDir/$classesDir"
+
+if (clover.equals("true")) {
+  use_clover = true
+  classes = "$buildDir/$cloverClassesDir"
+} else {
+  use_clover = false
+  classes = "$jalviewDir/$classesDir"
+}
+
+// configure classpath/args for j8/j11 compilation
+
+def jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
+def libDir
+def libDistDir
+def compile_source_compatibility
+def compile_target_compatibility
+
+ext {
+  getdownWebsiteDir = jalviewDir + '/' + getdown_website_dir + '/' + JAVA_VERSION
+  getdownDir = ""
+  reportRsyncCmd = false
+  buildDist = true
+  buildProperties = buildPropertiesFile
+  getdownLauncher = jalviewDir + '/' + getdown_lib_dir + '/' + getdown_launcher
+  switch (CHANNEL) {
+
+    case "BUILD":
+    // TODO: get bamboo build artifact URL for getdown artifacts
+    getdown_channel_base = bamboo_channelbase
+    getdown_channel_name = bamboo_planKey + '/'+JAVA_VERSION
+    getdown_app_base = bamboo_channelbase + '/'+ bamboo_planKey + bamboo_getdown_channel_suffix + '/'+JAVA_VERSION
+    getdown_app_dir = getdown_app_dir_alt
+    buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
+    break
+
+    case "RELEASE":
+    getdown_channel_name = CHANNEL.toLowerCase()
+    getdownDir = getdown_channel_name + "/" + JAVA_VERSION
+    getdown_app_base = getdown_channel_base + "/" + getdownDir
+    getdown_app_dir = getdown_app_dir_release
+    buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
+    reportRsyncCommand = true
+    break
+
+    case "ARCHIVE":
+    getdown_channel_name = CHANNEL.toLowerCase()+"/"+JALVIEW_VERSION
+    getdownDir = getdown_channel_name + "/" + JAVA_VERSION
+    getdown_app_base = getdown_channel_base + "/" + getdownDir
+    getdown_app_dir = getdown_app_dir_alt
+    if (!file(ARCHIVEDIR+"/"+packageDir).exists()) {
+      print "Must provide an ARCHIVEDIR value to produce an archive distribution"
+      exit
+    } else {
+      packageDir = ARCHIVEDIR + "/" + packageDir
+      buildProperties = ARCHIVEDIR +"/" + classesDir + "/" + buildPropertiesFile
+      buildDist = false
+    }
+    reportRsyncCommand = true
+    break
+
+    case "ARCHIVELOCAL":
+    getdown_channel_name = "archive" + "/" + JALVIEW_VERSION
+    getdownDir = getdown_channel_name + "/" + JAVA_VERSION
+    getdown_app_base = file(getdownWebsiteDir).toURI().toString()
+    getdown_app_dir = getdown_app_dir_alt
+    if (!file(ARCHIVEDIR+"/"+packageDir).exists()) {
+      print "Must provide an ARCHIVEDIR value to produce an archive distribution"
+      exit
+    } else {
+      packageDir = ARCHIVEDIR + "/" + packageDir
+      buildProperties = ARCHIVEDIR +"/" + classesDir + "/" + buildPropertiesFile
+      buildDist = false
+    }
+    reportRsyncCommand = true
+    getdownLauncher = jalviewDir + '/' + getdown_lib_dir + '/' + getdown_launcher_local
+    break
+
+    case "DEVELOP":
+    getdown_channel_name = CHANNEL.toLowerCase()
+    getdownDir = getdown_channel_name + "/" + JAVA_VERSION
+    getdown_app_base = getdown_channel_base + "/" + getdownDir
+    getdown_app_dir = getdown_app_dir_alt
+    buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
+    reportRsyncCommand = true
+    break
+
+    case "TEST-RELEASE":
+    getdown_channel_name = CHANNEL.toLowerCase()
+    getdownDir = getdown_channel_name + "/" + JAVA_VERSION
+    getdown_app_base = getdown_channel_base + "/" + getdownDir
+    getdown_app_dir = getdown_app_dir_alt
+    buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
+    reportRsyncCommand = true
+    break
+
+    case ~/^SCRATCH(|-[-\w]*)$/:
+    getdown_channel_name = CHANNEL
+    getdownDir = getdown_channel_name + "/" + JAVA_VERSION
+    getdown_app_base = getdown_channel_base + "/" + getdownDir
+    getdown_app_dir = getdown_app_dir_alt
+    buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
+    reportRsyncCommand = true
+    break
+
+    case "LOCAL":
+    getdown_app_base = file(getdownWebsiteDir).toURI().toString()
+    getdown_app_dir = getdown_app_dir_alt
+    buildProperties = jalviewDir + "/" + classesDir +"/" + buildPropertiesFile
+    getdownLauncher = jalviewDir + '/' + getdown_lib_dir + '/' + getdown_launcher_local
+    break
+
+    default: // something wrong specified
+    print("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
+    exit
+    break
+
+  }
+
+  println("Using a "+CHANNEL+" profile. appbase="+getdown_app_base)
+  getdownAppDir = getdownWebsiteDir + '/' + getdown_app_dir
+  //getdownJ11libDir = getdownWebsiteDir + '/' + getdown_j11lib_dir
+  getdownResourceDir = getdownWebsiteDir + '/' + getdown_resource_dir
+  getdownInstallDir = getdownWebsiteDir + '/' + getdown_install_dir
+  getdownFilesDir = jalviewDir + '/' + getdown_files_dir + '/' + JAVA_VERSION + '/'
+  getdownFilesInstallDir = getdownFilesDir+"/"+getdown_install_dir
+  /* compile without modules -- using classpath libraries
+  modules_compileClasspath = fileTree(dir: "$jalviewDir/$j11modDir", include: ["*.jar"])
+  modules_runtimeClasspath = modules_compileClasspath
+  */
+  gitHash = ""
+  gitBranch = ""
+}
+
+def JAVA_INTEGER_VERSION
+def additional_compiler_args = []
+// these are getdown.txt properties defined dependent on the JAVA_VERSION
+def getdown_alt_java_min_version
+def getdown_alt_java_max_version
+// this property is assigned below and expanded to multiple lines in the getdown task
+def getdown_alt_multi_java_location
+// this property is for the Java library used in eclipse
+def eclipse_java_runtime_name
+if (JAVA_VERSION.equals("1.8")) {
+  JAVA_INTEGER_VERSION = "8"
+  //libDir = j8libDir
+  libDir = j11libDir
+  libDistDir = j8libDir
+  compile_source_compatibility = 1.8
+  compile_target_compatibility = 1.8
+  getdown_alt_java_min_version = getdown_alt_java8_min_version
+  getdown_alt_java_max_version = getdown_alt_java8_max_version
+  getdown_alt_multi_java_location = getdown_alt_java8_txt_multi_java_location
+  eclipse_java_runtime_name = "JavaSE-1.8"
+} else if (JAVA_VERSION.equals("11")) {
+  JAVA_INTEGER_VERSION = "11"
+  libDir = j11libDir
+  libDistDir = j11libDir
+  compile_source_compatibility = 11
+  compile_target_compatibility = 11
+  getdown_alt_java_min_version = getdown_alt_java11_min_version
+  getdown_alt_java_max_version = getdown_alt_java11_max_version
+  getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
+  eclipse_java_runtime_name = "JavaSE-11"
+  /* compile without modules -- using classpath libraries
+  additional_compiler_args += [
+  '--module-path', ext.modules_compileClasspath.asPath,
+  '--add-modules', j11modules
+  ]
+  */
+} else if (JAVA_VERSION.equals("12") || JAVA_VERSION.equals("13")) {
+  JAVA_INTEGER_VERSION = JAVA_VERSION
+  libDir = j11libDir
+  libDistDir = j11libDir
+  compile_source_compatibility = JAVA_VERSION
+  compile_target_compatibility = JAVA_VERSION
+  getdown_alt_java_min_version = getdown_alt_java11_min_version
+  getdown_alt_java_max_version = getdown_alt_java11_max_version
+  getdown_alt_multi_java_location = getdown_alt_java11_txt_multi_java_location
+  eclipse_java_runtime_name = "JavaSE-11"
+  /* compile without modules -- using classpath libraries
+  additional_compiler_args += [
+  '--module-path', ext.modules_compileClasspath.asPath,
+  '--add-modules', j11modules
+  ]
+  */
+} else {
+  throw new GradleException("JAVA_VERSION=$JAVA_VERSION not currently supported by Jalview")
+}
+
+sourceSets {
+
+  main {
+    java {
+      srcDirs "$jalviewDir/$sourceDir"
+      outputDir = file("$classes")
+    }
+
+    resources {
+      srcDirs "$jalviewDir/$resourceDir"
+    }
+
+    jar.destinationDir = file("$jalviewDir/$packageDir")
+
+    compileClasspath = files(sourceSets.main.java.outputDir)
+    compileClasspath += fileTree(dir: "$jalviewDir/$libDir", include: ["*.jar"])
+
+    runtimeClasspath = compileClasspath
+  }
+  clover {
+    java {
+      srcDirs = [ cloverInstrDir ]
+      outputDir = file("${buildDir}/${cloverClassesDir}")
+    }
+
+    resources {
+      srcDirs = sourceSets.main.resources.srcDirs
+    }
+    compileClasspath = configurations.cloverRuntime + files( sourceSets.clover.java.outputDir )
+    compileClasspath += files(sourceSets.main.java.outputDir)
+    compileClasspath += sourceSets.main.compileClasspath
+    compileClasspath += fileTree(dir: "$jalviewDir/$utilsDir", include: ["**/*.jar"])
+    compileClasspath += fileTree(dir: "$jalviewDir/$libDir", include: ["*.jar"])
+
+    runtimeClasspath = compileClasspath
+  }
+
+  test {
+    java {
+      srcDirs "$jalviewDir/$testSourceDir"
+      outputDir = file("$jalviewDir/$testOutputDir")
+    }
+
+    resources {
+      srcDirs = sourceSets.main.resources.srcDirs
+    }
+
+    compileClasspath = files( sourceSets.test.java.outputDir )
+
+    if (use_clover) {
+      compileClasspath += sourceSets.clover.compileClasspath
+    } else {
+      compileClasspath += files(sourceSets.main.java.outputDir)
+    }
+
+    compileClasspath += fileTree(dir: "$jalviewDir/$utilsDir", include: ["**/*.jar"])
+    compileClasspath += fileTree(dir: "$jalviewDir/$libDir", include: ["*.jar"])
+
+    runtimeClasspath = compileClasspath
+  }
+}
+
+// clover bits
+dependencies {
+  if (use_clover) {
+    cloverCompile 'org.openclover:clover:4.3.1'
+    testCompile 'org.openclover:clover:4.3.1'
+  }
+}
+
+configurations {
+  cloverRuntime
+  cloverRuntime.extendsFrom cloverCompile
+}
+
+eclipse {
+  project {
+    name = "Jalview with gradle build"
+
+    natures 'org.eclipse.jdt.core.javanature',
+    'org.eclipse.jdt.groovy.core.groovyNature',
+    'org.eclipse.buildship.core.gradleprojectnature'
+
+    buildCommand 'org.eclipse.jdt.core.javabuilder'
+    buildCommand 'org.eclipse.buildship.core.gradleprojectbuilder'
+  }
+
+  classpath {
+    //defaultOutputDir = sourceSets.main.java.outputDir
+    def removeThese = []
+    configurations.each{ if (it.isCanBeResolved()) {
+      removeThese += it
+    }
+  }
+
+  minusConfigurations += removeThese
+  plusConfigurations = [ ]
+  file {
+
+    whenMerged { cp ->
+      def removeTheseToo = []
+      HashMap<String, Boolean> addedSrcPath = new HashMap<>();
+      cp.entries.each { entry ->
+        if (entry.kind == 'src') {
+          if (addedSrcPath.getAt(entry.path) || !(entry.path == "src" || entry.path == "test")) {
+            removeTheseToo += entry
+          } else {
+            addedSrcPath.putAt(entry.path, true)
+          }
+        }
+      }
+      cp.entries.removeAll(removeTheseToo)
+
+      print ("CP="+cp.inspect())
+
+      cp.entries += new Output("bin/main")
+      cp.entries += new Library(fileReference(helpParentDir))
+      cp.entries += new Library(fileReference(resourceDir))
+
+      HashMap<String, Boolean> addedLibPath = new HashMap<>();
+
+      // changing from sourcesets.main.classpath to specific Java version lib
+      //sourceSets.main.compileClasspath.each{
+      fileTree("$jalviewDir/$libDistDir").include("**/*.jar").include("*.jar").each {
+        //don't want to add outputDir as eclipse is using its own output dir in bin/main
+        if (it.isDirectory() || ! it.exists()) {
+          // don't add dirs to classpath
+          return
+        }
+        def itPath = it.toString()
+        if (itPath.startsWith(jalviewDirAbsolutePath+"/")) {
+          itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
+        }
+        if (addedLibPath.get(itPath)) {
+          //println("Not adding duplicate entry "+itPath)
+        } else {
+          //println("Adding entry "+itPath)
+          cp.entries += new Library(fileReference(itPath))
+          addedLibPath.put(itPath, true)
+        }
+      }
+
+      // changing from sourcesets.main.classpath to specific Java version lib
+      //sourceSets.test.compileClasspath.each{
+      fileTree(dir: "$jalviewDir/$utilsDir", include: ["**/*.jar"]).each {
+        //if ((it.isDirectory() || ! it.exists()) && ! (it.equals(sourceSets.main.java.outputDir))) {
+        //no longer want to add outputDir as eclipse is using its own output dir in bin/main
+        if (it.isDirectory() || ! it.exists()) {
+          // don't add dirs to classpath
+          return false // groovy "break" in .each loop
+        }
+        def itPath = it.toString()
+        if (itPath.startsWith(jalviewDirAbsolutePath+"/")) {
+          itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
+        }
+        if (addedLibPath.get(itPath)) {
+          // don't duplicate
+        } else {
+          def lib = new Library(fileReference(itPath))
+          // this doesn't work... yet.  Adding test=true attribute using withXml below
+          //def attrs = new Node(null, 'attributes', ["test":"true"])
+          //lib.appendNode(attrs) //
+          cp.entries += lib
+          addedLibPath.put(itPath, true)
+        }
+        }
+      }
+
+      // withXml changes ignored by buildship, these add the "test=true" attribute
+      withXml {
+        def node = it.asNode()
+
+        def srcTestAttributes
+        node.children().each{ cpe ->
+          def attributes = cpe.attributes()
+          if (attributes.get("kind") == "src" && attributes.get("path") == "test") {
+            srcTestAttributes = cpe.find { a -> a.name() == "attributes" }
+            return
+          }
+        }
+        def addTestAttribute = true
+        srcTestAttributes.each{a ->
+          if (a.name() == "attribute" && a.attributes().getAt("name") == "test") {
+            addTestAttribute = false
+          }
+        }
+        if (addTestAttribute) {
+          srcTestAttributes.append(new Node(null, "attribute", [name:"test", value:"true"]))
+        }
+
+        node.children().each{ cpe ->
+          def attributes = cpe.attributes()
+          if (attributes.get("kind") == "lib" && attributes.get("path").startsWith("utils/")) {
+            cpe.appendNode('attributes')
+            .appendNode('attribute', [name:"test", value:"true"])
+          }
+        }
+      } // withXML
+    } // file
+
+    containers 'org.eclipse.buildship.core.gradleclasspathcontainer'
+  } // classpath
+
+  jdt {
+    // for the IDE, use java 11 compatibility
+    sourceCompatibility = compile_source_compatibility
+    targetCompatibility = compile_target_compatibility
+    javaRuntimeName = eclipse_java_runtime_name
+
+    file {
+      withProperties { props ->
+        def jalview_prefs = new Properties()
+        def ins = new FileInputStream(jalviewDirAbsolutePath+"/"+eclipse_extra_jdt_prefs_file)
+        jalview_prefs.load(ins)
+        ins.close()
+        jalview_prefs.forEach { t, v ->
+          if (props.getAt(t) == null) {
+            props.putAt(t, v)
+          }
+        }
+      }
+    }
+  }
+
+  //synchronizationTasks eclipseClasspath
+  //autoBuildTasks eclipseClasspath
+}
+
+task cloverInstr() {
+  // only instrument source, we build test classes as normal
+  inputs.files files (sourceSets.main.allJava) // , fileTree(dir:"$jalviewDir/$testSourceDir", include: ["**/*.java"]))
+  outputs.dir cloverInstrDir
+
+  doFirst {
+    delete cloverInstrDir
+    def argsList = ["--initstring", "${buildDir}/clover/clover.db",
+    "-d", "${buildDir}/${cloverSourcesInstrDir}"]
+    argsList.addAll(inputs.files.files.collect({ file ->
+      file.absolutePath
+    }))
+    String[] args = argsList.toArray()
+    println("About to instrument "+args.length +" files")
+    com.atlassian.clover.CloverInstr.mainImpl(args)
+  }
+}
+
+
+task cloverReport {
+  group = "Verification"
+    description = "Createst the Clover report"
+    inputs.dir "${buildDir}/clover"
+    outputs.dir "${reportsDir}/clover"
+    onlyIf {
+      file("${buildDir}/clover/clover.db").exists()
+    }
+  doFirst {
+    def argsList = ["--initstring", "${buildDir}/clover/clover.db",
+    "-o", "${reportsDir}/clover"]
+    String[] args = argsList.toArray()
+    com.atlassian.clover.reporters.html.HtmlReporter.runReport(args)
+
+    // and generate ${reportsDir}/clover/clover.xml
+    args = ["--initstring", "${buildDir}/clover/clover.db",
+    "-o", "${reportsDir}/clover/clover.xml"].toArray()
+    com.atlassian.clover.reporters.xml.XMLReporter.runReport(args)
+  }
+}
+
+// end clover bits
+
+
+compileJava {
+
+  doFirst {
+    sourceCompatibility = compile_source_compatibility
+    targetCompatibility = compile_target_compatibility
+    options.compilerArgs = additional_compiler_args
+    print ("Setting target compatibility to "+targetCompatibility+"\n")
+  }
+
+}
+
+compileTestJava {
+  if (use_clover) {
+    dependsOn compileCloverJava
+    classpath += configurations.cloverRuntime
+  } else {
+    classpath += sourceSets.main.runtimeClasspath
+  }
+  doFirst {
+    sourceCompatibility = compile_source_compatibility
+    targetCompatibility = compile_target_compatibility
+    options.compilerArgs = additional_compiler_args
+    print ("Setting target compatibility to "+targetCompatibility+"\n")
+  }
+}
+
+
+compileCloverJava {
+
+  doFirst {
+    sourceCompatibility = compile_source_compatibility
+    targetCompatibility = compile_target_compatibility
+    options.compilerArgs += additional_compiler_args
+    print ("Setting target compatibility to "+targetCompatibility+"\n")
+  }
+  classpath += configurations.cloverRuntime
+}
+
+clean {
+  delete sourceSets.main.java.outputDir
+}
+
+cleanTest {
+  delete sourceSets.test.java.outputDir
+  delete cloverInstrDir
+}
+
+// format is a string like date.format("dd MMMM yyyy")
+def getDate(format) {
+  def date = new Date()
+  return date.format(format)
+}
+
+task setGitVals {
+  def hashStdOut = new ByteArrayOutputStream()
+  exec {
+    commandLine "git", "rev-parse", "--short", "HEAD"
+    standardOutput = hashStdOut
+    ignoreExitValue true
+  }
+
+  def branchStdOut = new ByteArrayOutputStream()
+  exec {
+    commandLine "git", "rev-parse", "--abbrev-ref", "HEAD"
+    standardOutput = branchStdOut
+    ignoreExitValue true
+  }
+
+  project.ext.gitHash = hashStdOut.toString().trim()
+  project.ext.gitBranch = branchStdOut.toString().trim()
+
+  outputs.upToDateWhen { false }
+}
+
+task createBuildProperties(type: WriteProperties) {
+  dependsOn setGitVals
+  inputs.dir("$jalviewDir/$sourceDir")
+  inputs.dir("$classes")
+  inputs.dir("$jalviewDir/$resourceDir")
+  outputFile (buildProperties)
+  // taking time specific comment out to allow better incremental builds
+  comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd HH:mm:ss")
+  //comment "--Jalview Build Details--\n"+getDate("yyyy-MM-dd")
+  property "BUILD_DATE", getDate("HH:mm:ss dd MMMM yyyy")
+  property "VERSION", JALVIEW_VERSION
+  property "INSTALLATION", INSTALLATION+" git-commit:"+project.ext.gitHash+" ["+project.ext.gitBranch+"]"
+  outputs.file(outputFile)
+}
+
+def buildingHTML = "$jalviewDir/$docDir/building.html"
+task deleteBuildingHTML(type: Delete) {
+  delete buildingHTML
+}
+
+task convertBuildingMD(type: Exec) {
+  dependsOn deleteBuildingHTML
+  def buildingMD = "$jalviewDir/$docDir/building.md"
+  def css = "$jalviewDir/$docDir/github.css"
+
+  def pandoc = null
+  pandoc_exec.split(",").each {
+    if (file(it.trim()).exists()) {
+      pandoc = it.trim()
+      return true
+    }
+  }
+
+  def hostname = "hostname".execute().text.trim()
+  if ((pandoc == null || ! file(pandoc).exists()) && hostname.equals("jv-bamboo")) {
+    pandoc = System.getProperty("user.home")+"/buildtools/pandoc/bin/pandoc"
+  }
+
+  doFirst {
+    if (pandoc != null && file(pandoc).exists()) {
+        commandLine pandoc, '-s', '-o', buildingHTML, '--metadata', 'pagetitle="Building Jalview from Source"', '--toc', '-H', css, buildingMD
+    } else {
+        println("Cannot find pandoc. Skipping convert building.md to HTML")
+        throw new StopExecutionException()
+    }
+  }
+
+  ignoreExitValue true
+
+  inputs.file(buildingMD)
+  inputs.file(css)
+  outputs.file(buildingHTML)
+}
+clean {
+  delete buildingHTML
+}
+
+task syncDocs(type: Sync) {
+  dependsOn convertBuildingMD
+  def syncDir = "$classes/$docDir"
+  from fileTree("$jalviewDir/$docDir")
+  into syncDir
+
+}
+
+def helpFile = "$classes/$helpDir/help.jhm"
+
+task copyHelp(type: Copy) {
+  def inputDir = "$jalviewDir/$helpParentDir/$helpDir"
+  def outputDir = "$classes/$helpDir"
+  from(inputDir) {
+    exclude '**/*.gif'
+      exclude '**/*.jpg'
+      exclude '**/*.png'
+      filter(ReplaceTokens, beginToken: '$$', endToken: '$$', tokens: ['Version-Rel': JALVIEW_VERSION,'Year-Rel': getDate("yyyy")])
+  }
+  from(inputDir) {
+    include '**/*.gif'
+      include '**/*.jpg'
+      include '**/*.png'
+  }
+  into outputDir
+
+  inputs.dir(inputDir)
+  outputs.files(helpFile)
+  outputs.dir(outputDir)
+}
+
+task syncLib(type: Sync) {
+  def syncDir = "$classes/$libDistDir"
+  from fileTree("$jalviewDir/$libDistDir")
+  into syncDir
+}
+
+task syncResources(type: Sync) {
+  from "$jalviewDir/$resourceDir"
+  include "**/*.*"
+  exclude "install4j"
+  into "$classes"
+  preserve {
+    include "**"
+  }
+}
+
+task prepare {
+  dependsOn syncResources
+  dependsOn syncDocs
+  dependsOn copyHelp
+}
+
+
+//testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
+test {
+  dependsOn prepare
+  dependsOn compileJava
+  if (use_clover) {
+    dependsOn cloverInstr
+  }
+
+  if (use_clover) {
+    print("Running tests " + (use_clover?"WITH":"WITHOUT") + " clover [clover="+use_clover+"]\n")
+  }
+
+  useTestNG() {
+    includeGroups testngGroups
+    preserveOrder true
+    useDefaultListeners=true
+  }
+
+  workingDir = jalviewDir
+  //systemProperties 'clover.jar' System.properties.clover.jar
+  sourceCompatibility = compile_source_compatibility
+  targetCompatibility = compile_target_compatibility
+  jvmArgs += additional_compiler_args
+  print ("Setting target compatibility to "+targetCompatibility+"\n")
+}
+
+task buildIndices(type: JavaExec) {
+  dependsOn copyHelp
+  classpath = sourceSets.main.compileClasspath
+  main = "com.sun.java.help.search.Indexer"
+  workingDir = "$classes/$helpDir"
+  def argDir = "html"
+  args = [ argDir ]
+  inputs.dir("$workingDir/$argDir")
+
+  outputs.dir("$classes/doc")
+  outputs.dir("$classes/help")
+  outputs.file("$workingDir/JavaHelpSearch/DOCS")
+  outputs.file("$workingDir/JavaHelpSearch/DOCS.TAB")
+  outputs.file("$workingDir/JavaHelpSearch/OFFSETS")
+  outputs.file("$workingDir/JavaHelpSearch/POSITIONS")
+  outputs.file("$workingDir/JavaHelpSearch/SCHEMA")
+  outputs.file("$workingDir/JavaHelpSearch/TMAP")
+}
+
+task compileLinkCheck(type: JavaCompile) {
+  options.fork = true
+  classpath = files("$jalviewDir/$utilsDir")
+  destinationDir = file("$jalviewDir/$utilsDir")
+  source = fileTree(dir: "$jalviewDir/$utilsDir", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
+
+  inputs.file("$jalviewDir/$utilsDir/HelpLinksChecker.java")
+  inputs.file("$jalviewDir/$utilsDir/HelpLinksChecker.java")
+  outputs.file("$jalviewDir/$utilsDir/HelpLinksChecker.class")
+  outputs.file("$jalviewDir/$utilsDir/BufferedLineReader.class")
+}
+
+def helplinkscheckertouchfile = file("$jalviewDir/$utilsDir/HelpLinksChecker.touch")
+task linkCheck(type: JavaExec) {
+  dependsOn prepare, compileLinkCheck
+  classpath = files("$jalviewDir/$utilsDir")
+  main = "HelpLinksChecker"
+  workingDir = jalviewDir
+  def help = "$classes/$helpDir"
+  args = [ "$classes/$helpDir", "-nointernet" ]
+
+  doLast {
+    helplinkscheckertouchfile.createNewFile()
+  }
+
+  inputs.dir("$classes/$helpDir")
+  outputs.file(helplinkscheckertouchfile)
+}
+
+// import the pubhtmlhelp target
+ant.properties.basedir = "$jalviewDir"
+ant.properties.helpBuildDir = jalviewDirAbsolutePath+"/$classes/$helpDir"
+ant.importBuild "$utilsDir/publishHelp.xml"
+
+
+task cleanPackageDir(type: Delete) {
+  delete fileTree("$jalviewDir/$packageDir").include("*.jar")
+}
+
+jar {
+  dependsOn linkCheck
+  dependsOn buildIndices
+  dependsOn createBuildProperties
+
+  manifest {
+    attributes "Main-Class": mainClass,
+    "Permissions": "all-permissions",
+    "Application-Name": "Jalview Desktop",
+    "Codebase": application_codebase
+  }
+
+  destinationDir = file("$jalviewDir/$packageDir")
+  archiveName = rootProject.name+".jar"
+
+  exclude "cache*/**"
+  exclude "*.jar"
+  exclude "*.jar.*"
+  exclude "**/*.jar"
+  exclude "**/*.jar.*"
+
+  inputs.dir("$classes")
+  outputs.file("$jalviewDir/$packageDir/$archiveName")
+}
+
+task copyJars(type: Copy) {
+  from fileTree("$classes").include("**/*.jar").include("*.jar").files
+  into "$jalviewDir/$packageDir"
+}
+
+// doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
+task syncJars(type: Sync) {
+  from fileTree("$jalviewDir/$libDistDir").include("**/*.jar").include("*.jar").files
+  into "$jalviewDir/$packageDir"
+  preserve {
+    include jar.archiveName
+  }
+}
+
+task makeDist {
+  group = "build"
+  description = "Put all required libraries in dist"
+  // order of "cleanPackageDir", "copyJars", "jar" important!
+  jar.mustRunAfter cleanPackageDir
+  syncJars.mustRunAfter cleanPackageDir
+  dependsOn cleanPackageDir
+  dependsOn syncJars
+  dependsOn jar
+  outputs.dir("$jalviewDir/$packageDir")
+}
+
+task cleanDist {
+  dependsOn cleanPackageDir
+  dependsOn cleanTest
+  dependsOn clean
+}
+
+shadowJar {
+  group = "distribution"
+  if (buildDist) {
+    dependsOn makeDist
+  }
+  from ("$jalviewDir/$libDistDir") {
+    include("*.jar")
+  }
+  manifest {
+    attributes 'Implementation-Version': JALVIEW_VERSION
+  }
+  mainClassName = shadowJarMainClass
+  mergeServiceFiles()
+  classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
+  minimize()
+}
+
+task getdownWebsite() {
+  group = "distribution"
+  description = "Create the getdown minimal app folder, and website folder for this version of jalview. Website folder also used for offline app installer"
+  if (buildDist) {
+    dependsOn makeDist
+  }
+
+       // clean the getdown website and files dir before creating getdown folders
+       delete project.ext.getdownWebsiteDir
+       delete project.ext.getdownFilesDir
+
+  def getdownWebsiteResourceFilenames = []
+  def getdownTextString = ""
+  def getdownResourceDir = project.ext.getdownResourceDir
+  def getdownAppDir = project.ext.getdownAppDir
+  def getdownResourceFilenames = []
+
+  doFirst {
+    copy {
+      from buildProperties
+      rename(buildPropertiesFile, getdown_build_properties)
+      into project.ext.getdownAppDir
+    }
+    getdownWebsiteResourceFilenames += getdown_app_dir+"/"+getdown_build_properties
+
+    // go through properties looking for getdown_txt_...
+    def props = project.properties.sort { it.key }
+       if (getdown_alt_java_min_version.length() > 0) {
+               props.put("getdown_txt_java_min_version", getdown_alt_java_min_version)
+       }
+       if (getdown_alt_java_max_version.length() > 0) {
+               props.put("getdown_txt_java_max_version", getdown_alt_java_max_version)
+       }
+       props.put("getdown_txt_multi_java_location", getdown_alt_multi_java_location)
+
+    props.put("getdown_txt_appbase", getdown_app_base)
+    props.each{ prop, val ->
+      if (prop.startsWith("getdown_txt_") && val != null) {
+        if (prop.startsWith("getdown_txt_multi_")) {
+          def key = prop.substring(18)
+          val.split(",").each{ v ->
+            def line = key + " = " + v + "\n"
+            getdownTextString += line
+          }
+        } else {
+          // file values rationalised
+          if (val.indexOf('/') > -1 || prop.startsWith("getdown_txt_resource")) {
+            def r = null
+            if (val.indexOf('/') == 0) {
+              // absolute path
+              r = file(val)
+            } else if (val.indexOf('/') > 0) {
+              // relative path (relative to jalviewDir)
+              r = file( jalviewDir + '/' + val )
+            }
+            if (r.exists()) {
+              val = getdown_resource_dir + '/' + r.getName()
+              getdownWebsiteResourceFilenames += val
+              getdownResourceFilenames += r.getPath()
+            }
+          }
+          if (! prop.startsWith("getdown_txt_resource")) {
+            def line = prop.substring(12) + " = " + val + "\n"
+            getdownTextString += line
+          }
+        }
+      }
+    }
+
+    getdownWebsiteResourceFilenames.each{ filename ->
+      getdownTextString += "resource = "+filename+"\n"
+    }
+    getdownResourceFilenames.each{ filename ->
+      copy {
+        from filename
+        into project.ext.getdownResourceDir
+      }
+    }
+
+    def codeFiles = []
+    fileTree(file(packageDir)).each{ f ->
+      if (f.isDirectory()) {
+        def files = fileTree(dir: f, include: ["*"]).getFiles()
+        codeFiles += files
+      } else if (f.exists()) {
+        codeFiles += f
+      }
+    }
+    codeFiles.sort().each{f ->
+      def line = "code = " + getdown_app_dir + '/' + f.getName() + "\n"
+      getdownTextString += line
+      copy {
+        from f.getPath()
+        into project.ext.getdownAppDir
+      }
+    }
+
+    // NOT USING MODULES YET, EVERYTHING SHOULD BE IN dist
+    /*
+    if (JAVA_VERSION.equals("11")) {
+    def j11libFiles = fileTree(dir: "$jalviewDir/$j11libDir", include: ["*.jar"]).getFiles()
+    j11libFiles.sort().each{f ->
+    def line = "code = " + getdown_j11lib_dir + '/' + f.getName() + "\n"
+    getdownTextString += line
+    copy {
+    from f.getPath()
+    into project.ext.getdownJ11libDir
+    }
+    }
+    }
+     */
+
+    // getdown-launcher.jar should not be in main application class path so the main application can move it when updated.  Listed as a resource so it gets updated.
+    //getdownTextString += "class = " + file(getdownLauncher).getName() + "\n"
+    getdownTextString += "resource = " + getdown_launcher_new + "\n"
+    getdownTextString += "class = " + mainClass + "\n"
+
+    def getdown_txt = file(project.ext.getdownWebsiteDir + "/getdown.txt")
+    getdown_txt.write(getdownTextString)
+
+    def launch_jvl = file(project.ext.getdownWebsiteDir + "/" + getdown_launch_jvl)
+    launch_jvl.write("appbase="+props.get("getdown_txt_appbase"))
+
+    copy {
+      from getdownLauncher
+      rename(file(getdownLauncher).getName(), getdown_launcher_new)
+      into project.ext.getdownWebsiteDir
+    }
+
+    copy {
+      from getdownLauncher
+      if (file(getdownLauncher).getName() != getdown_launcher) {
+        rename(file(getdownLauncher).getName(), getdown_launcher)
+      }
+      into project.ext.getdownWebsiteDir
+    }
+
+    if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
+      copy {
+        from getdown_txt
+        from getdownLauncher
+        from getdownWebsiteDir+"/"+getdown_build_properties
+        if (file(getdownLauncher).getName() != getdown_launcher) {
+          rename(file(getdownLauncher).getName(), getdown_launcher)
+        }
+        into getdownInstallDir
+      }
+
+      copy {
+        from getdownInstallDir
+        into getdownFilesInstallDir
+      }
+    }
+
+    copy {
+      from getdown_txt
+      from launch_jvl
+      from getdownLauncher
+      from getdownWebsiteDir+"/"+getdown_build_properties
+      if (file(getdownLauncher).getName() != getdown_launcher) {
+        rename(file(getdownLauncher).getName(), getdown_launcher)
+      }
+      into getdownFilesDir
+    }
+
+    copy {
+         from getdownResourceDir
+      into project.ext.getdownFilesDir + '/' + getdown_resource_dir
+    }
+  }
+
+  if (buildDist) {
+    inputs.dir(jalviewDir + '/' + packageDir)
+  }
+  outputs.dir(project.ext.getdownWebsiteDir)
+  outputs.dir(project.ext.getdownFilesDir)
+}
+
+task getdownDigest(type: JavaExec) {
+  group = "distribution"
+  description = "Digest the getdown website folder"
+  dependsOn getdownWebsite
+  doFirst {
+    classpath = files(getdownWebsiteDir + '/' + getdown_launcher)
+  }
+  main = "com.threerings.getdown.tools.Digester"
+  args project.ext.getdownWebsiteDir
+  inputs.dir(project.ext.getdownWebsiteDir)
+  outputs.file(project.ext.getdownWebsiteDir + '/' + "digest2.txt")
+}
+
+task getdown() {
+  group = "distribution"
+  description = "Create the minimal and full getdown app folder for installers and website and create digest file"
+  dependsOn getdownDigest
+  doLast {
+    if (reportRsyncCommand) {
+      def fromDir = getdownWebsiteDir + (getdownWebsiteDir.endsWith("/")?"":"/")
+      def toDir = getdown_rsync_dest + "/" + getdownDir + (getdownDir.endsWith("/")?"":"/")
+      println "LIKELY RSYNC COMMAND:"
+      println "mkdir -p '$toDir'\nrsync -avh --delete '$fromDir' '$toDir'"
+      if (RUNRSYNC == "true") {
+        exec {
+          commandLine "mkdir", "-p", toDir
+        }
+        exec {
+          commandLine "rsync", "-avh", "--delete", fromDir, toDir
+        }
+      }
+    }
+  }
+}
+
+clean {
+  delete project.ext.getdownWebsiteDir
+  delete project.ext.getdownFilesDir
+}
+
+install4j {
+  def install4jHomeDir = "/opt/install4j"
+  def hostname = "hostname".execute().text.trim()
+  if (hostname.equals("jv-bamboo")) {
+    install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
+  } else if (OperatingSystem.current().isMacOsX()) {
+    install4jHomeDir = '/Applications/install4j.app/Contents/Resources/app'
+    if (! file(install4jHomeDir).exists()) {
+      install4jHomeDir = System.getProperty("user.home")+install4jHomeDir
+    }
+  } else if (OperatingSystem.current().isLinux()) {
+    install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
+  }
+  installDir = file(install4jHomeDir)
+  mediaTypes = Arrays.asList(install4jMediaTypes.split(","))
+  if (install4jFaster.equals("true")) {
+    faster = true
+  }
+}
+
+def install4jConf
+def macosJavaVMDir
+def macosJavaVMTgz
+def windowsJavaVMDir
+def windowsJavaVMTgz
+def install4jDir = "$jalviewDir/$install4jResourceDir"
+def install4jConfFile = "jalview-installers-java"+JAVA_VERSION+".install4j"
+install4jConf = "$install4jDir/$install4jConfFile"
+
+task copyInstall4jTemplate(type: Copy) {
+  macosJavaVMDir = System.env.HOME+"/buildtools/jre/openjdk-java_vm/getdown/macos-jre"+JAVA_VERSION+"/jre"
+  macosJavaVMTgz = System.env.HOME+"/buildtools/jre/openjdk-java_vm/install4j/tgz/macos-jre"+JAVA_VERSION+".tar.gz"
+  windowsJavaVMDir = System.env.HOME+"/buildtools/jre/openjdk-java_vm/getdown/windows-jre"+JAVA_VERSION+"/jre"
+  windowsJavaVMTgz = System.env.HOME+"/buildtools/jre/openjdk-java_vm/install4j/tgz/windows-jre"+JAVA_VERSION+".tar.gz"
+  from (install4jDir) {
+    include install4jTemplate
+    rename (install4jTemplate, install4jConfFile)
+    filter(ReplaceTokens, beginToken: '', endToken: '', tokens: ['9999999999': JAVA_VERSION])
+    filter(ReplaceTokens, beginToken: '$$', endToken: '$$',
+    tokens: [
+    'JAVA_VERSION': JAVA_VERSION,
+    'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
+    'VERSION': JALVIEW_VERSION,
+    'MACOS_JAVA_VM_DIR': macosJavaVMDir,
+    'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
+    'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
+    'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
+    'INSTALL4JINFOPLISTFILEASSOCIATIONS': install4jInfoPlistFileAssociations,
+    'COPYRIGHT_MESSAGE': install4jCopyrightMessage,
+    'MACOS_BUNDLE_ID': install4jMacOSBundleId,
+    'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
+    'GETDOWN_DIST_DIR': getdown_app_dir,
+    'GETDOWN_ALT_DIR': getdown_app_dir_alt,
+    'GETDOWN_INSTALL_DIR': getdown_install_dir
+    ]
+    )
+    if (OSX_KEYPASS=="") {
+      filter(ReplaceTokens, beginToken: 'codeSigning macEnabled="', endToken: '"', tokens: ['true':'codeSigning macEnabled="false"'])
+      filter(ReplaceTokens, beginToken: 'runPostProcessor="true" ',endToken: 'Processor', tokens: ['post':'runPostProcessor="false" postProcessor'])
+    }
+  }
+  into install4jDir
+  outputs.files(install4jConf)
+
+  doLast {
+    // include file associations in installer
+    def installerFileAssociationsXml = file("$install4jDir/$install4jInstallerFileAssociations").text
+    ant.replaceregexp(
+      byline: false,
+      flags: "s",
+      match: '<action name="EXTENSIONS_REPLACED_BY_GRADLE".*?</action>',
+      replace: installerFileAssociationsXml,
+      file: install4jConf
+    )
+    /*
+    // include uninstaller applescript app files in dmg
+    def installerDMGUninstallerXml = file("$install4jDir/$install4jDMGUninstallerAppFiles").text
+    ant.replaceregexp(
+    byline: false,
+    flags: "s",
+    match: '<file name="UNINSTALL_OLD_JALVIEW_APP_REPLACED_IN_GRADLE" file=.*?>',
+    replace: installerDMGUninstallerXml,
+    file: install4jConf
+    )
+     */
+  }
+}
+
+task installers(type: com.install4j.gradle.Install4jTask) {
+  group = "distribution"
+  description = "Create the install4j installers"
+  dependsOn getdown
+  dependsOn copyInstall4jTemplate
+  projectFile = file(install4jConf)
+  println("Using projectFile "+projectFile)
+  variables = [majorVersion: version.substring(2, 11), build: 001, OSX_KEYSTORE: OSX_KEYSTORE, JSIGN_SH: JSIGN_SH]
+  destination = "$jalviewDir/$install4jBuildDir/$JAVA_VERSION"
+  buildSelected = true
+
+  if (OSX_KEYPASS) {
+    macKeystorePassword=OSX_KEYPASS
+
+  }
+
+  inputs.dir(project.ext.getdownWebsiteDir)
+  inputs.file(install4jConf)
+  inputs.dir(macosJavaVMDir)
+  inputs.dir(windowsJavaVMDir)
+  outputs.dir("$jalviewDir/$install4jBuildDir/$JAVA_VERSION")
+
+}
+
+clean {
+  delete install4jConf
+}
+
+task sourceDist (type: Tar) {
+  
+  def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
+  def outputFileName = project.name + "_" + VERSION_UNDERSCORES + ".tar.gz"
+  // cater for buildship < 3.1 [3.0.1 is max version in eclipse 2018-09]
+  try {
+    archiveFileName = outputFileName
+  } catch (Exception e) {
+    archiveName = outputFileName
+  }
+  
+  compression Compression.GZIP
+  
+  into project.name
+
+  def EXCLUDE_FILES=["build/*","bin/*","test-output/","test-reports","tests","clover*/*"
+  ,".*"
+  ,"benchmarking/*"
+  ,"**/.*"
+  ,"*.class"
+  ,"**/*.class","$j11modDir/**/*.jar","appletlib","**/*locales"
+  ,"*locales/**",
+  ,"utils/InstallAnywhere"] 
+  def PROCESS_FILES=[   "AUTHORS",
+  "CITATION",
+  "FEATURETODO",
+  "JAVA-11-README",
+  "FEATURETODO",
+  "LICENSE",
+  "**/README",
+  "RELEASE",
+  "THIRDPARTYLIBS","TESTNG",
+  "build.gradle",
+  "gradle.properties",
+  "**/*.java",
+  "**/*.html",
+  "**/*.xml",
+  "**/*.gradle",
+  "**/*.groovy",
+  "**/*.properties",
+  "**/*.perl",
+  "**/*.sh"]
+
+  from(jalviewDir) {
+    exclude (EXCLUDE_FILES)
+    include (PROCESS_FILES)
+    filter(ReplaceTokens, beginToken: '$$', endToken: '$$', tokens: ['Version-Rel': JALVIEW_VERSION,'Year-Rel': getDate("yyyy")])
+  }
+  from(jalviewDir) {
+    exclude (EXCLUDE_FILES)
+    exclude (PROCESS_FILES)
+  exclude ("appletlib")
+  exclude ("**/*locales")
+  exclude ("*locales/**")
+  exclude ("utils/InstallAnywhere")
+
+    exclude (getdown_files_dir)
+  exclude (getdown_website_dir)
+
+  // exluding these as not using jars as modules yet
+  exclude ("$j11modDir/**/*.jar")
+}
+//  from (jalviewDir) {
+//    // explicit includes for stuff that seemed to not get included
+//    include(fileTree("test/**/*."))
+//    exclude(EXCLUDE_FILES)
+//    exclude(PROCESS_FILES)
+//  }
+}
+
+task helppages  {
+  dependsOn copyHelp
+  dependsOn pubhtmlhelp
+  
+  inputs.dir("$classes/$helpDir")
+  outputs.dir("$helpOutputDir")
+}
diff --git a/doc/NEW_JARS b/doc/NEW_JARS
new file mode 100644 (file)
index 0000000..d7b1c56
--- /dev/null
@@ -0,0 +1,52 @@
+## this command gets a list of jars in j11lib/ that are not present or different content in lib/.  The output is below.
+#for y in j11lib/*.jar; do x=${y##*/}; if [ \! -e ./lib/$x ] || ! diff ./lib/$x $y; then echo $y; fi; done
+#
+j11lib/FastInfoset.jar
+j11lib/getdown-core.jar
+j11lib/groovy-2.5.7.jar
+j11lib/groovy-ant-2.5.7.jar
+j11lib/groovy-bsf-2.5.7.jar
+j11lib/groovy-cli-commons-2.5.7.jar
+j11lib/groovy-cli-picocli-2.5.7.jar
+j11lib/groovy-console-2.5.7.jar
+j11lib/groovy-datetime-2.5.7.jar
+j11lib/groovy-dateutil-2.5.7.jar
+j11lib/groovy-docgenerator-2.5.7.jar
+j11lib/groovy-groovydoc-2.5.7.jar
+j11lib/groovy-groovysh-2.5.7.jar
+j11lib/groovy-jaxb-2.5.7.jar
+j11lib/groovy-jmx-2.5.7.jar
+j11lib/groovy-json-2.5.7.jar
+j11lib/groovy-json-direct-2.5.7.jar
+j11lib/groovy-jsr223-2.5.7.jar
+j11lib/groovy-macro-2.5.7.jar
+j11lib/groovy-nio-2.5.7.jar
+j11lib/groovy-servlet-2.5.7.jar
+j11lib/groovy-sql-2.5.7.jar
+j11lib/groovy-swing-2.5.7.jar
+j11lib/groovy-templates-2.5.7.jar
+j11lib/groovy-test-2.5.7.jar
+j11lib/groovy-test-junit5-2.5.7.jar
+j11lib/groovy-testng-2.5.7.jar
+j11lib/groovy-xml-2.5.7.jar
+j11lib/istack-commons-runtime.jar
+j11lib/javax.activation-MODULE.jar
+j11lib/javax.annotation-api-MODULE.jar
+j11lib/javax.jws-api-1.1.jar
+j11lib/javax.servlet-api-MODULE.jar
+j11lib/javax.xml.rpc-api-1.1.2.jar
+j11lib/javax.xml.soap-api.jar
+j11lib/jaxb-api-java9.jar
+j11lib/jaxb-runtime.jar
+j11lib/jaxws-api.jar
+j11lib/jersey-client-1.19.1.jar
+j11lib/jersey-core-1.19.1.jar
+j11lib/jersey-json-1.19.1.jar
+j11lib/jsr311-api-1.1.1.jar
+j11lib/mimepull.jar
+j11lib/policy.jar
+j11lib/stax-ex.jar
+j11lib/stax2-api-MODULE.jar
+j11lib/streambuffer.jar
+j11lib/txw2.jar
+j11lib/wsdl4j-MODULE.jar
diff --git a/doc/building.html b/doc/building.html
deleted file mode 100755 (executable)
index ecde4d8..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-<html>
-<!--
- * 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.
--->
-<head>
-<title>Building Jalview from Source</title>
-</head>
-<body>
-<h1>Building Jalview from Source</h1>
-<P>
-<p>
-You will need the following (hopefully):<br>
-<ul>
-<li>Java development kit (JDK1.8 is now the recommended platform for developing with Jalview.</li>
-<li>Ant (1.7 or later will be needed for some of the jarsigning tools).</li>
-</ul>
-With any luck, after setting your paths and JAVA_HOME correctly, you
-just need to change to the Jalview directory and run ant (this works
-from eclipse too, but NetBeans is a bit trickier).
-<pre>
-   ant
-</pre>
-
-</p>
-<p><strong>Building a webstart version of jalview</strong></p>
-Jalview depends on several libraries contained in the libs directory
-of the distribution.  In order to access them, they must all be signed
-jars - using the same jarsigner key as jalview itself. There is a
-build target in ant to make the signed jar files in a directory called
-dist. But first you need to make your own key:
-<p><strong>Making your own key</strong></p>
-
-  <p>The ant 'makefulldist' target assumes that a keystore exists in
-    a directory 'keys'. To make a key accessible using the default
-    settings in the build.xml file then make the keys directory and add
-    the jarsigner key with the following :</p>
-  <pre>mkdir keys</pre>
-  <pre>keytool -genkey -keystore keys/.keystore -keypass alignmentisfun
-  -storepass alignmentisfun -sigalg SHA1withRSA -keyalg RSA -alias jalview</pre>
-  <em>(you will have to answer some personal questions here)</em>
-  <pre>ant makedist -DWebStartLocation="file://.pathtojalviewsource./dist" -Dapplication.codebase="*"</pre>
-  <p>This should eventually generate a jalview.jnlp file in ./dist
-    along with a set of signed jars using the jalview key). In order to
-    test locally via webstart you'll now need to add 'file:/' to your
-    java webstart security exception list. Then:</p>
-  <pre>javaws file://.pathtojalviewsource./dist/jalview.jnlp</pre>
-  <p>Please remember to remove that entry afterwards, since it will leave
-  your system vulnerable to malicious code.
-  </p>
-  <p>
-               <strong>Building the JalviewLite applet<br>
-               </strong> The JalviewLite applet is compiled using a subset of the packages in
-               the src directory (specifically: MCView, and jalview.{datamodel,
-               analysis, appletgui, utils, schemes, api, structure}, and
-               com.stevesoft.*). Once compiled, these class files are obfuscated to
-               make the code run efficiently. To compile the applet Jar, use the
-               makeApplet task - optionally passing in a 'donotobfuscate' property to
-               the ant build (e.g. -Ddonotobfuscate=true) to disable obfuscation. </p><p>
-               The ant target 'pubapplet' can be used to compile install the
-               jalviewApplet.jar and any dependent jars (under appletlib) into a copy
-               of the examples directory created under the <em>outputDir</em> build
-               property (which defaults to the 'dist' directory).
-       </p>
-       <p>
-<h1>using IDEs to build Jalview</h1>
-       <p>The Jalview source distribution includes project definitions for
-               Eclipse, Netbeans and some rather ancient Borland JBuilder .jpx
-               project files. These files should be sufficient to set up basic source
-               folders and build paths, but you will need to ensure that all .jar
-               files in the lib and appletlib directories are added to the build path
-               for your IDE project, and that the 'buildindices' target in Jalview's
-               build.xml is executed with the 'outputDir' ant property set to the
-               directory where the IDE expects to place compiled classes ('classes'
-               directory for eclipse, 'build/classes' for netbeans).</p>
-       <p>Note: It is generally not recommended that you distribute build
-               artefacts that were generated automatically via an IDE's own packaging
-               mechanism (e.g. Netbeans' executable Jar and dependent lib directory).
-               The hand-crafted ant build.xml is (currently) the only officially
-               supported method of building distributable versions of Jalview.</p>
-<address>
-<a href="mailto:help@jalview.org">Jalview development team</a>
-</address>
-</body>
-</html>
diff --git a/doc/building.md b/doc/building.md
new file mode 100644 (file)
index 0000000..5f5fa6d
--- /dev/null
@@ -0,0 +1,501 @@
+# Building Jalview from Source
+
+## tl;dr
+
+```
+# download
+git clone http://source.jalview.org/git/jalview.git
+# compile
+cd jalview
+gradle shadowJar
+# run
+java -jar build/libs/jalview-all-11.jar
+
+# and/or create launcher
+gradle getdown
+# use launcher
+cd getdown/files
+java -jar getdown-launcher.jar . jalview
+```
+
+
+## Setting up
+
+The method here is described in terms of using a command line.  You can easily do this on linux or in a Terminal window in macOS.  You can do it in Windows.
+
+* Java 11 compliant JDK
+* gradle 5.2 or above
+* git
+
+> The versions and installation methods here are just suggestions (which we have tested 
+so are known to work).  If you need or wish to use different implementations (particularly 
+you might need a bespoke JDK if you are on an exotic architecture) then the general 
+build instructions should work with any gradle 5+.  You should be able to compile the 
+bytecode with any JDK Java 11+.  The resulting bytecode (in particular the shadow jar) 
+should be runnable in any JRE Java 1.8+.  Remember that because Jalview and the getdown launcher 
+are Java bytecode you can build on one system where you might have gradle, and run 
+on another where you don't (JRE 1.8+ required).
+
+### Java 11 compliant JDK
+
+#### All platforms
+We recommend obtaining an OpenJDK JDK 11 (since 11 is the long term support release) from AdoptOpenJDK: <https://adoptopenjdk.net/?variant=openjdk11&jvmVariant=hotspot>, either the *Installer* or `.zip`/`.tar.gz` variants whichever you prefer (if you're not sure, choose the *Installer*).
+
+>##### Alternative/CLI install of AdoptOpenJDK 11
+>
+>You can also install adoptopenjdk11 using either `brew` (macOS), `choco` (Windows)
+(see the section on `gradle` and `git` for more informaiton on `brew` and `choco`) 
+or `yum` or `apt` (Linux):
+>
+>###### alternative for MacOS and Homebrew
+>```
+>brew tap adoptopenjdk/openjdk
+>brew cask install adoptopenjdk11
+>```
+>
+>###### alternative for Windows and Chocolatey
+>```
+>choco install adoptopenjdk11
+>```
+>
+>###### alternative for Linux with yum/apt
+>
+>see <https://adoptopenjdk.net/installation.html#linux-pkg>
+
+
+### gradle and git
+You should be able to install the latest (or sufficiently recent) versions of gradle and git using your OS package manager.
+
+#### MacOS
+we recommend using `brew`, which can be installed following the instructions at <https://brew.sh/>.
+After installing `brew`, open a Terminal window and type in (using an Administrator privileged user):
+
+```bash
+brew install gradle git
+```
+
+or if you aready have them installed but need to upgrade the version:
+
+```bash
+brew upgrade gradle git
+```
+
+#### Windows
+
+we suggest using the **Chocolatey** package manager.  See install instructions at <https://chocolatey.org/>, and you will just need
+
+```bash
+choco install gradle
+choco install git
+```
+
+Alternatively, you could install a real `bash` shell and install both `gradle` and `git` through `apt-get`.
+See <https://devblogs.microsoft.com/commandline/bash-on-ubuntu-on-windows-download-now-3/> 
+for how to install the ubuntu bash shell in Windows 10.
+
+Another alternative would be to install them separately. For `gradle` follow the instructions at <https://gradle.org/install/>, and for `git` here are a couple of suggestions: Git for Windows <https://gitforwindows.org/>.
+Getting the individual installs working together on the command line will be trickier 
+so we recommend using Chocolatey or bash.
+
+
+#### Linux
+this will depend on which distribution you're using.
+
+##### For *Debian* based distributions (e.g. Mint, Ubuntu, Debian)
+run
+
+```bash
+ sudo apt-get install gradle git
+```
+
+##### for RPM-based distributions (e.g. Fedora, CentOS, RedHat)
+run
+
+```bash
+sudo yum install gradle git
+```
+
+If you have some other version of linux you'll probably be able to work it out!
+
+
+
+
+## Downloading the Jalview source tree
+
+This can be done with `git`.
+On the command line, change directory to where you want to download Jalview's build-tree 
+top level directory.  Then run
+
+```bash
+git clone http://source.jalview.org/git/jalview.git
+```
+
+You'll get some progress output and after a minute or two you should have the full 
+Jalview build-tree in the folder `jalview`.
+
+### What's in the source tree?
+
+Jalview is a mature product with its codebase going back many years.  As such it doesn't 
+have a folder structure that most new gradle projects would have, so you might not 
+find everything in the place you might expect.  Here's a brief description of what 
+you might find in the main folders under the `jalview` tree.
+
+Within the `jalview` folder you will find (of possible interest):
+
+  dir/ or file       | contains                                                   
+---------------------|----------------------------------------------------------------------------------------------------------------
+ `bin/`              | used by eclipse for compiled classes -- no need to touch this
+ `build/`            | the gradle build dir
+ `classes/`          | contains the compiled Java classes for the Jalview application
+ `dist/`             | assembled `.jar` files needed to run Jalview application
+ `examples/`         | example input files usable by Jalview
+ `getdown/`          | the libraries used by the Javliew launcher (getdown)
+ `getdown/src/`      | our modified source for `getdown`
+ `getdown/website/`  | the assembled "download" folder used by getdown for downloads/upgrades
+ `getdown/files/`    | the minimal fileset to launch the Jalview launcher, which can then download the rest of the Jalview application
+ `help/`             | the help documents
+ `j8lib/`            | libraries needed to run Jalview under Java 1.8
+ `j11lib/`           | libraries needed to run Jalivew under Java 11
+ `resource/`         | non-java resources used in the Jalview application
+ `src/`              | the Jalview application source `.java` files
+ `test/`             | Test class source files
+ `utils/`            | helper applications used in the build process
+ `utils/install4j/`  | files used by the packaging tool, install4j
+ `build.gradle`      | the build file used by gradle
+ `gradle.properties` | configurable properties for the build process
+
+Note that you need a Java 11 JDK to compile Jalview whether your target build is Java 1.8 or Java 11.
+
+
+## Building Jalview
+
+You will need to have the Java 11 `javac` in your path, or alternatively you can configure 
+gradle to know where this is by putting
+
+```
+org.gradle.java.home=/path_to_jdk_directory
+```
+in the `gradle.properties` file.
+
+> *You may want to see some of the other properties you can change at the end of this document.*
+
+### Minimal Jalview Build
+
+To compile the necessary class files, just run
+
+```bash
+gradle compileJava
+```
+to compile the classes into the `classes` folder.
+You should now be able to run the Jalview application directly with
+
+```bash
+java -cp "classes:resources:help:j11lib/*" jalview.bin.Jalview
+```
+
+You can also run with an automatic large memory setting (which will set the maximum 
+memory heap of the Jalview JVM to 90% of your local physical memory) and docked icon setting 
+(if possible in your OS) with
+
+```bash
+java -cp "classes:resources:help:j11lib/*" jalview.bin.Launcher
+```
+
+>*You must use just "`j11lib/*`" and not "`j11lib/*.jar`" as this is a special Java 
+classpath argument wildcard interpreted by `java`, **not** a shell expansion wildcard interpreted 
+by the shell.*
+
+Note that `jalview.bin.Launcher` is a simplified launcher class that re-launches `jalview.bin.Jalview` 
+with the same JRE (*not* the same JVM instance), classpath and arguments, but with an automatically determined `-Xmx...` 
+memory setting if one hasn't been provided.
+
+### Jalview in a Jar File
+
+To package the `classes`, `resources`, and `help` into one jar, you can run
+
+```bash
+gradle jar
+```
+which assembles the Jalview classes and resources into `dist/jalview.jar`
+
+To run this, use
+
+```bash
+java -cp "dist/jalview.jar:j11lib/*" jalview.bin.Jalview
+```
+
+### Distributed Jar Files
+
+To simplify this, all required `.jar` files can be assembled into the `dist` folder 
+using
+
+```bash
+gradle makeDist
+```
+which puts all required jar files into `dist` so you can run with
+
+```bash
+java -cp "dist/*" jalview.bin.Jalview
+```
+
+### Single *shadow* Jar File
+
+The shadow jar file is a single `.jar` that contains all required classes and resources from `jalview.jar` 
+and all of the supporting libraries in `j11lib/*.jar` merged into one `.jar` archive 
+file.  A default launching class (`MAIN-CLASS: jalview.bin.Launcher`) is specified in the `.jar` 
+manifest file (`META/MANIFEST.MF`) so a start class doesn't need to be specified.
+
+Build the shadow jar file in `build/lib/jalview-all-11.jar` with
+
+```bash
+gradle shadowJar
+```
+and run it with
+
+```bash
+java -jar build/lib/jalview-all-11.jar
+```
+
+Because no arguments are required, most OSes will associate a `.jar` file with the 
+`java` application (if this has been installed through the OS and not just a local 
+unzip) as a `-jar` argument so you may find you can launch `jalview-all-11.jar` 
+just by double-clicking on it)!
+
+> The `shadowJar` task is not a requirement for any other task, so to build the shadow 
+jar file you must specify the `shadowJar` task.
+
+> The shadow jar file represents probably the simplest way to distribute the Jalview application to machines that already have a Java 11 installed, 
+although without the many and compelling benefits of the `getdown` launcher.
+
+
+### Building the `getdown` launcher
+
+We have made significant customisations to the `getdown` launcher which you can find 
+in `getdown/src/getdown`.
+
+> You don't need to build this afresh as the required `gradle-core.jar` 
+and `gradle-launcher.jar` files are already distributed in `j11lib` and `getdown/lib` but if you want to, then 
+you'll need a working Maven and also a Java 8 JDK.  Ensure the Java 8 `javac` is forefront 
+in your path and do
+>
+>```bash
+>cd getdown/src/getdown
+>mvn clean package -Dgetdown.host.whitelist="jalview.org,*.jalview.org"
+>```
+> and you will find the required `.jar` files in `core/target/gradle-core-XXX.jar` 
+and `launcher/target/gradle-launcher-XXX.jar`.  The `gradle-core.jar` should then be copied 
+to all three of the `j8lib`, `j11lib` and `getdown/lib` folders, whilst the `gradle-launcher.jar` only 
+needs to be copied to `getdown/lib`.
+>
+>The `mvn` command should ideally include the `-Dgetdown.host.whitelist=*.jalview.org` setting. 
+ This, and the necessary file copying commands, can be found in `getdown/src/getdown/mvn_cmd`.
+
+To assemble Jalview with `getdown` use the following gradle task:
+
+```bash
+gradle getdown
+```
+
+This puts all the necessary files to launch Jalview with `getdown`
+into `getdown/website/11/`.  This could be treated as the reference folder 
+for `getdown`, which is where a getdown launcher will check to see if the Jalview application 
+files it has are up to date, and download if they aren't or it simply doesn't have 
+them.
+
+A minimal getdown-launcher can be found in `getdown/files/11/` which checks its up-to-date 
+status with (the absolute path to) `getdown/website/11/`.
+
+This can be launched with
+
+```bash
+java -jar getdown/files/11/getdown-launcher.jar getdown/files/11/ jalview
+```
+
+> We've already met the `-jar file.jar` arguments.  The next argument is the working folder for 
+getdown, and the final argument, "`jalview`", is a getdown application id (only "`jalview`" 
+is defined here).
+
+
+### Running tests
+
+There are substantial tests written for Jalview that use TestNG, which you can run with
+
+```bash
+gradle test
+```
+
+These normally take around 5 - 10 minutes to complete and outputs its full results into 
+the `tests/` folder.  A summary of results should appear in your console.
+
+You can run different defined groups of tests with
+
+```bash
+gradle test -PtestngGroups=Network
+```
+
+Available groups are Functional (default), Network, External.
+
+
+### Installer packaging with *install4j*
+
+Jalview is currently using *install4j* <https://www.ej-technologies.com/products/install4j/overview.html> 
+as its installer packaging tool.
+
+If you have a licensed installation of *install4j* you can build Jalview installers 
+by running
+
+```bash
+gradle installers
+```
+
+though you may need to fiddle with the `install4j` and `copyInstall4jTemplate` tasks 
+in `build.gradle` file to point to your installation of *install4j* and also to bundled 
+JREs if you want to bundle those into the installers.
+
+If you want more details, get in touch on our development mailing list <jalview-dev@jalview.org>. 
+Sign up at <http://www.compbio.dundee.ac.uk/mailman/listinfo/jalview-dev>.
+
+
+## Building in Eclipse
+
+We develop in Eclipse, and support settings to develop and save Jalview source code 
+in our preferred style.  We also support running the Jalview application, debugging 
+and running tests with TestNG from within Eclipse.
+
+To get Jalview set up as a project in Eclipse, we recommend using at least the 2019-03 
+version of Eclipse IDE for Java Developers which you can download from the Eclipse 
+website: <https://www.eclipse.org/downloads/>
+
+Once installed, we also recommend installing several plugins from the Eclipse Marketplace.
+
+To do so, launch Eclipse, and go to Help->Eclipse Marketplace...
+
+Search for and install:
+
+1. Buildship Gradle Integration 3.0 (or greater)
+1. Groovy Development Tools 3.4.0 (or greater)
+1. TestNG for Eclipse (optional -- only needed if you want to run tests from Eclipse)
+
+> At time of writing, TestNG for Eclipse does not show up in the Eclipse Marketplace 
+as the latest released version does not install in Eclipse 2019-03.
+However, you can install a working beta of TestNG for Eclipse by going to
+>
+Help->Install New Software...
+>
+and entering
+>
+`TestNG Eclipse Composite P2 Repo - http://beust.com/eclipse-beta`
+>
+into the *Work with* box and click on the *Add...* button.
+>
+Eclipse might pause for a bit with the word *Pending* in the table below at this point, but it will eventually list TestNG with 
+a selection box under the *Name* column.
+>
+Select *TestNG* and carry on through the 
+install process to install the TestNG plugin.
+
+After installing the plugins, it is a good to get Java 11 set up in Eclipse as the default JRE.
+
+To do this go to Preferences (Eclipse->Preferences in macOS, File->Preferences 
+on Windows or Window->Preferences on Linux) and find
+
+Java -> Installed JREs
+
+If your Java 11 installation is not listed, click on
+
+*Add* -> Standard VM -> *Next*
+
+and enter the JRE home.  You can browse to where it was installed. Give it a name (like "AdoptOpenJDK 11").  Select this JDK 
+as the default JRE and click on *Apply and Close*.
+
+
+You can now import Jalview.  It is important to import 
+Jalview as a Gradle project (not as a Java project), so go to
+
+File->Import...
+
+find and select
+
+Gradle->Existing Gradle Project
+
+and then click on the *Next >* button.
+
+In the following options, it is the *Project Root Directory* you should set to be the 
+`jalview` folder that git downloaded.  Then you can click on the *Finish* button.
+
+
+## Gradle properties
+
+There are a lot of properties configured in `gradle.properties` which we strongly recommend 
+being left as they are unless you have a specific problem with the build process.
+
+There are a few gradle properties you might want to set on the command line with the 
+`-P` flag when building a version of Jalview with specific requirements:
+
+#### `JAVA_VERSION`
+This changes the *target* java bytecode version
+> NOTE that you will need to use a Java 11 (or greater) JDK Java compiler to build 
+Jalview for any byte-code target version.
+
+Valid values are `11` and `1.8`.
+
+e.g.
+
+```bash
+gradle shadowJar -PJAVA_VERSION=1.8
+```
+
+When using `-PJAVA_VERSION=1.8` the libraries from `j8lib` (instead of `j11lib`) will be used in the compile  
+and runtime classpath and also used in the `makeDist` build step.  Where a Java version of `11` is used in folder and file names, it will 
+instead use `1.8`.  Also if you are building installer packages with *install4j* the 
+package builder will look for JRE 1.8 bundles to package in the installers.
+
+> Note that continued development of Jalview will assume a Java 11+ runtime environment, 
+the 2.11.0 release will run under a Java 1.8 JRE with a few minor features disabled.
+
+#### `CHANNEL`
+This changes the `appbase` setting in `getdown.txt` (`appbase` is where the getdown launcher 
+looks to see if there's an updated file) to point to a particular Jalview 
+channel.  Expected values are `FILE`, `STABLE`, `DEVELOPMENT`, or a specific version 
+of Jalview like `2.11` or `2.10.5`.
+
+A value of `FILE` behaves differently to the other expected values and will use a local 
+file-system scheme URI instead of a Jalview release channel.  This `file:` scheme URI 
+uses an absolute path to the `getdown/website/<JAVA_VERSION>`
+
+On a regular development machine, this property will default to `LOCAL`.
+
+e.g.
+```bash
+gradle getdown -PCHANNEL=DEVELOPMENT
+```
+
+#### `MEDIA_TYPES`
+If you are building *install4j* installers (requires *install4j* to be installed) then this property specifies a comma-separated 
+list of media types (i.e. platform specific installers) *install4j* should actually build.
+
+Currently the valid values are
+`linuxDeb`, 
+`linuxRPM`, 
+`macosArchive`, 
+`unixArchive`, 
+`unixInstaller`, 
+`windows` 
+
+The default value is all of them.
+
+e.g.
+```bash
+gradle installers -PJAVA_VERSION=1.8 -PMEDA_TYPE=macosArchive
+```
+
+To get an up-to-date list of possible values, you can run
+
+```bash
+perl -n -e 'm/^\s*<(\w+)[^>]*\bmediaFileName=/ && print "$1\n";' utils/install4j/install4j_template.install4j  | sort -u
+```
+in the `jalview` root folder.
+
+
+---
+[Jalview Development Team](mailto:help@jalview.org)
diff --git a/doc/convert.sh b/doc/convert.sh
new file mode 100644 (file)
index 0000000..fbfa17d
--- /dev/null
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+pandoc -s -o building.html building.md --metadata pagetitle="Building Jalview from Source" --toc -H github.css
diff --git a/doc/getdown_installation_structure.pdf b/doc/getdown_installation_structure.pdf
new file mode 100644 (file)
index 0000000..9fc0dc6
Binary files /dev/null and b/doc/getdown_installation_structure.pdf differ
diff --git a/doc/github.css b/doc/github.css
new file mode 100644 (file)
index 0000000..9018d8a
--- /dev/null
@@ -0,0 +1,662 @@
+<style>
+@font-face {
+  font-family: octicons-link;
+  src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff');
+}
+
+body {
+  -webkit-text-size-adjust: 100%;
+  text-size-adjust: 100%;
+  color: #333;
+  font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+  font-size: 16px;
+  line-height: 1.6;
+  word-wrap: break-word;
+  width: 728px;
+  max-width: 99%;
+  box-sizing: border-box;
+  padding: 30px 30px 8rem 30px;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+body a {
+  background-color: transparent;
+}
+
+body a:active,
+body a:hover {
+  outline: 0;
+}
+
+body strong {
+  font-weight: bold;
+}
+
+body h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+body img {
+  border: 0;
+}
+
+body hr {
+  box-sizing: content-box;
+  height: 0;
+}
+
+body pre {
+  overflow: auto;
+}
+
+body code,
+body kbd,
+body pre {
+  font-family: monospace, monospace;
+  font-size: 1em;
+}
+
+body input {
+  color: inherit;
+  font: inherit;
+  margin: 0;
+}
+
+body html input[disabled] {
+  cursor: default;
+}
+
+body input {
+  line-height: normal;
+}
+
+body input[type="checkbox"] {
+  box-sizing: border-box;
+  padding: 0;
+}
+
+body table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+body td,
+body th {
+  padding: 0;
+}
+
+body * {
+  box-sizing: border-box;
+}
+
+body input {
+  font: 13px / 1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+}
+
+body a {
+  color: #4078c0;
+  text-decoration: none;
+}
+
+body a:hover,
+body a:active {
+  text-decoration: underline;
+}
+
+body hr {
+  height: 0;
+  margin: 15px 0;
+  overflow: hidden;
+  background: transparent;
+  border: 0;
+  border-bottom: 1px solid #ddd;
+}
+
+body hr:before {
+  display: table;
+  content: "";
+}
+
+body hr:after {
+  display: table;
+  clear: both;
+  content: "";
+}
+
+body h1,
+body h2,
+body h3,
+body h4,
+body h5,
+body h6 {
+  margin-top: 15px;
+  margin-bottom: 15px;
+  line-height: 1.1;
+}
+
+body h1 {
+  font-size: 30px;
+}
+
+body h2 {
+  font-size: 21px;
+}
+
+body h3 {
+  font-size: 16px;
+}
+
+body h4 {
+  font-size: 14px;
+}
+
+body h5 {
+  font-size: 12px;
+}
+
+body h6 {
+  font-size: 11px;
+}
+
+body blockquote {
+  margin: 0;
+}
+
+body ul,
+body ol {
+  padding: 0;
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+body ol ol,
+body ul ol {
+  list-style-type: lower-roman;
+}
+
+body ul ul ol,
+body ul ol ol,
+body ol ul ol,
+body ol ol ol {
+  list-style-type: lower-alpha;
+}
+
+body dd {
+  margin-left: 0;
+}
+
+body code {
+  font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
+  font-size: 12px;
+}
+
+body pre {
+  margin-top: 0;
+  margin-bottom: 0;
+  font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
+}
+
+body .select::-ms-expand {
+  opacity: 0;
+}
+
+body .octicon {
+  font: normal normal normal 16px/1 octicons-link;
+  display: inline-block;
+  text-decoration: none;
+  text-rendering: auto;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+
+body .octicon-link:before {
+  content: '\f05c';
+}
+
+body:before {
+  display: table;
+  content: "";
+}
+
+body:after {
+  display: table;
+  clear: both;
+  content: "";
+}
+
+body>*:first-child {
+  margin-top: 0 !important;
+}
+
+body>*:last-child {
+  margin-bottom: 0 !important;
+}
+
+body a:not([href]) {
+  color: inherit;
+  text-decoration: none;
+}
+
+body .anchor {
+  display: inline-block;
+  padding-right: 2px;
+  margin-left: -18px;
+}
+
+body .anchor:focus {
+  outline: none;
+}
+
+body h1,
+body h2,
+body h3,
+body h4,
+body h5,
+body h6 {
+  margin-top: 1em;
+  margin-bottom: 16px;
+  font-weight: bold;
+  line-height: 1.4;
+}
+
+body h1 .octicon-link,
+body h2 .octicon-link,
+body h3 .octicon-link,
+body h4 .octicon-link,
+body h5 .octicon-link,
+body h6 .octicon-link {
+  color: #000;
+  vertical-align: middle;
+  visibility: hidden;
+}
+
+body h1:hover .anchor,
+body h2:hover .anchor,
+body h3:hover .anchor,
+body h4:hover .anchor,
+body h5:hover .anchor,
+body h6:hover .anchor {
+  text-decoration: none;
+}
+
+body h1:hover .anchor .octicon-link,
+body h2:hover .anchor .octicon-link,
+body h3:hover .anchor .octicon-link,
+body h4:hover .anchor .octicon-link,
+body h5:hover .anchor .octicon-link,
+body h6:hover .anchor .octicon-link {
+  visibility: visible;
+}
+
+body h1 {
+  padding-bottom: 0.3em;
+  font-size: 1.75em;
+  line-height: 1.2;
+}
+
+body h1 .anchor {
+  line-height: 1;
+}
+
+body h2 {
+  padding-bottom: 0.3em;
+  font-size: 1.5em;
+  line-height: 1.225;
+}
+
+body h2 .anchor {
+  line-height: 1;
+}
+
+body h3 {
+  font-size: 1.25em;
+  line-height: 1.43;
+}
+
+body h3 .anchor {
+  line-height: 1.2;
+}
+
+body h4 {
+  font-size: 1em;
+}
+
+body h4 .anchor {
+  line-height: 1.2;
+}
+
+body h5 {
+  font-size: 1em;
+}
+
+body h5 .anchor {
+  line-height: 1.1;
+}
+
+body h6 {
+  font-size: 1em;
+  color: #777;
+}
+
+body h6 .anchor {
+  line-height: 1.1;
+}
+
+body p,
+body blockquote,
+body ul,
+body ol,
+body dl,
+body table,
+body pre {
+  margin-top: 0;
+  margin-bottom: 16px;
+}
+
+body hr {
+  height: 4px;
+  padding: 0;
+  margin: 16px 0;
+  background-color: #e7e7e7;
+  border: 0 none;
+}
+
+body ul,
+body ol {
+  padding-left: 2em;
+}
+
+body ul ul,
+body ul ol,
+body ol ol,
+body ol ul {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+body li>p {
+  margin-top: 16px;
+}
+
+body dl {
+  padding: 0;
+}
+
+body dl dt {
+  padding: 0;
+  margin-top: 16px;
+  font-size: 1em;
+  font-style: italic;
+  font-weight: bold;
+}
+
+body dl dd {
+  padding: 0 16px;
+  margin-bottom: 16px;
+}
+
+body blockquote {
+  padding: 0 15px;
+  color: #777;
+  border-left: 4px solid #ddd;
+}
+
+body blockquote>:first-child {
+  margin-top: 0;
+}
+
+body blockquote>:last-child {
+  margin-bottom: 0;
+}
+
+body table {
+  display: block;
+  width: 100%;
+  overflow: auto;
+  word-break: normal;
+  word-break: keep-all;
+}
+
+body table th {
+  font-weight: bold;
+}
+
+body table th,
+body table td {
+  padding: 6px 13px;
+  border: 1px solid #ddd;
+}
+
+body table tr {
+  background-color: #fff;
+  border-top: 1px solid #ccc;
+}
+
+body table tr:nth-child(2n) {
+  background-color: #f8f8f8;
+}
+
+body img {
+  max-width: 100%;
+  box-sizing: content-box;
+  background-color: #fff;
+}
+
+body code {
+  padding: 0;
+  padding-top: 0;
+  padding-bottom: 0;
+  margin: 0;
+  font-size: 85%;
+  background-color: rgba(0,0,0,0.04);
+  border-radius: 3px;
+}
+
+body code:before,
+body code:after {
+  letter-spacing: -0.2em;
+  content: "\00a0";
+}
+
+body pre>code {
+  padding: 0;
+  margin: 0;
+  font-size: 100%;
+  word-break: normal;
+  white-space: pre;
+  background: transparent;
+  border: 0;
+}
+
+body .highlight {
+  margin-bottom: 16px;
+}
+
+body .highlight pre,
+body pre {
+  padding: 16px;
+  overflow: auto;
+  font-size: 85%;
+  line-height: 1.45;
+  background-color: #f7f7f7;
+  border-radius: 3px;
+}
+
+body .highlight pre {
+  margin-bottom: 0;
+  word-break: normal;
+}
+
+body pre {
+  word-wrap: normal;
+}
+
+body pre code {
+  display: inline;
+  max-width: initial;
+  padding: 0;
+  margin: 0;
+  overflow: initial;
+  line-height: inherit;
+  word-wrap: normal;
+  background-color: transparent;
+  border: 0;
+}
+
+body pre code:before,
+body pre code:after {
+  content: normal;
+}
+
+body kbd {
+  display: inline-block;
+  padding: 3px 5px;
+  font-size: 11px;
+  line-height: 10px;
+  color: #555;
+  vertical-align: middle;
+  background-color: #fcfcfc;
+  border: solid 1px #ccc;
+  border-bottom-color: #bbb;
+  border-radius: 3px;
+  box-shadow: inset 0 -1px 0 #bbb;
+}
+
+body .pl-c {
+  color: #969896;
+}
+
+body .pl-c1,
+body .pl-s .pl-v {
+  color: #0086b3;
+}
+
+body .pl-e,
+body .pl-en {
+  color: #795da3;
+}
+
+body .pl-s .pl-s1,
+body .pl-smi {
+  color: #333;
+}
+
+body .pl-ent {
+  color: #63a35c;
+}
+
+body .pl-k {
+  color: #a71d5d;
+}
+
+body .pl-pds,
+body .pl-s,
+body .pl-s .pl-pse .pl-s1,
+body .pl-sr,
+body .pl-sr .pl-cce,
+body .pl-sr .pl-sra,
+body .pl-sr .pl-sre {
+  color: #183691;
+}
+
+body .pl-v {
+  color: #ed6a43;
+}
+
+body .pl-id {
+  color: #b52a1d;
+}
+
+body .pl-ii {
+  background-color: #b52a1d;
+  color: #f8f8f8;
+}
+
+body .pl-sr .pl-cce {
+  color: #63a35c;
+  font-weight: bold;
+}
+
+body .pl-ml {
+  color: #693a17;
+}
+
+body .pl-mh,
+body .pl-mh .pl-en,
+body .pl-ms {
+  color: #1d3e81;
+  font-weight: bold;
+}
+
+body .pl-mq {
+  color: #008080;
+}
+
+body .pl-mi {
+  color: #333;
+  font-style: italic;
+}
+
+body .pl-mb {
+  color: #333;
+  font-weight: bold;
+}
+
+body .pl-md {
+  background-color: #ffecec;
+  color: #bd2c00;
+}
+
+body .pl-mi1 {
+  background-color: #eaffea;
+  color: #55a532;
+}
+
+body .pl-mdr {
+  color: #795da3;
+  font-weight: bold;
+}
+
+body .pl-mo {
+  color: #1d3e81;
+}
+
+body kbd {
+  display: inline-block;
+  padding: 3px 5px;
+  font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
+  line-height: 10px;
+  color: #555;
+  vertical-align: middle;
+  background-color: #fcfcfc;
+  border: solid 1px #ccc;
+  border-bottom-color: #bbb;
+  border-radius: 3px;
+  box-shadow: inset 0 -1px 0 #bbb;
+}
+
+body .task-list-item {
+  list-style-type: none;
+}
+
+body .task-list-item+.task-list-item {
+  margin-top: 3px;
+}
+
+body .task-list-item input {
+  margin: 0 0.35em 0.25em -1.6em;
+  vertical-align: middle;
+}
+
+body :checked+.radio-label {
+  z-index: 1;
+  position: relative;
+  border-color: #4078c0;
+}
+</style>
diff --git a/examples/exampleFile.jvp b/examples/exampleFile.jvp
new file mode 100644 (file)
index 0000000..458cc0f
Binary files /dev/null and b/examples/exampleFile.jvp differ
diff --git a/getdown/lib/getdown-core.jar b/getdown/lib/getdown-core.jar
new file mode 100644 (file)
index 0000000..bbe1476
Binary files /dev/null and b/getdown/lib/getdown-core.jar differ
diff --git a/getdown/lib/getdown-launcher-local.jar b/getdown/lib/getdown-launcher-local.jar
new file mode 100644 (file)
index 0000000..4a36222
Binary files /dev/null and b/getdown/lib/getdown-launcher-local.jar differ
diff --git a/getdown/lib/getdown-launcher.jar b/getdown/lib/getdown-launcher.jar
new file mode 100644 (file)
index 0000000..85ede0c
Binary files /dev/null and b/getdown/lib/getdown-launcher.jar differ
diff --git a/getdown/src/getdown/AUTHORS b/getdown/src/getdown/AUTHORS
new file mode 100644 (file)
index 0000000..5f389d0
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# This is the official list of the AUTHORS of Getdown for copyright purposes.
+#
+# This is not the full list of contributors, see
+# https://github.com/threerings/getdown/graphs/contributors
+# for the full list of contributors.
+#
+# Contributors assign copyright of their work to the authors listed in the this file to keep life
+# simple.
+
+Michael Bayne <mdb@samskivert.com>
+Ray Greenwell <ray@threerings.net>
diff --git a/getdown/src/getdown/CHANGELOG.md b/getdown/src/getdown/CHANGELOG.md
new file mode 100644 (file)
index 0000000..098651e
--- /dev/null
@@ -0,0 +1,180 @@
+# Getdown Releases
+
+## 1.8.3 - Unreleased
+
+* Added support for `nresource` resources which must be jar files that contain native libraries.
+  Prior to launching the application, these resources will be unpacked and their contents added to
+  the `java.library.path` system property.
+
+* When the app is updated to require a new version of the JVM, that JVM will be downloaded and used
+  immediately during that app invocation (instead of one invocation later). Via PR#169.
+
+* When a custom JVM is installed, old JVM files will be deleted prior to unpacking the new JVM. Via
+  PR#170.
+
+* Number of concurrent downloads now defaults to num-cores minus one. Though downloads are I/O
+  bound rather than CPU bound, this still turns out to be a decent default.
+
+* Avoid checking for proxy config if `https.proxyHost` is set. This matches existing behavior when
+  `http.proxyHost` is set.
+
+* Added support for proxy authentication. A deployment must also use the
+  `com.threerings.getdown.spi.ProxyAuth` service provider interface to persist the proxy
+  credentials supplied by the user. Otherwise they will be requested every time Getdown runs, which
+  is not a viable user experience.
+
+## 1.8.2 - Nov 27, 2018
+
+* Fixed a data corruption bug introduced at last minute into 1.8.1 release. Oops.
+
+## 1.8.1 - Nov 26, 2018
+
+* If both an `appbase` and `appdir` are provided via some means (bootstrap properties file, system
+  property, etc.) and the app dir does not yet exist, Getdown will create it.
+
+* Added `max_concurrent_downloads` setting to `getdown.txt`. Controls what you would expect.
+  Defaults to two.
+
+* `bootstrap.properties` can now contain system properties which will be set prior to running
+  Getdown. They must be prefixed by `sys.`: for example `sys.silent = true` will set the `silent`
+  system property to `true`.
+
+* If Getdown is run in a headless JVM, it will avoid showing a UI but will attempt to install and
+  launch the application anyhow. Note that passing `-Dsilent` will override this behavior (because
+  in silent mode the default is only to install the app, not also launch it).
+
+* Fixed issue with `appid` not being properly used when specified via command line arg.
+
+* Fixed issue with running Getdown on single CPU systems (or virtual systems). It was attempting to
+  create a thread pool of size zero, which failed.
+
+* Fixed issue with backslashes (or other regular expression escape characters) in environment
+  variables being substituted into app arguments.
+
+## 1.8.0 - Oct 19, 2018
+
+* Added support for manually specifying the thread pool size via `-Dthread_pool_size`. Also reduced
+  the default thread pool size to `num_cpus-1` from `num_cpus`.
+
+* Added support for bundling a `bootstrap.properties` file with the Getdown jar file, which can
+  specify defaults for `appdir`, `appbase` and `appid`.
+
+* Added support for a host URL whitelist. Getdown can be custom built to refuse to operate with any
+  URL that does not match the built-time-specified whitelist. See `core/pom.xml` for details.
+
+* Removed the obsolete support for running Getdown in a signed applet. Applets are no longer
+  supported by any widely used browser.
+
+* Split the project into multiple Maven modules. See the notes on [migrating from 1.7 to 1.8] for
+  details.
+
+* A wide variety of small cleanups resulting from a security review generously performed by a
+  prospective user. This includes various uses of deterministic locales and encodings instead of
+  the platform default locale/encoding, in cases where platform/locale-specific behavior is not
+  desired or needed.
+
+* Made use of `appid` fall back to main app class if no `appid`-specific class is specified.
+
+* Added support for marking resources as executable (via `xresource`).
+
+* Fixed issue where entire tracking URL was being URL encoded.
+
+* Changed translations to avoid the use of the term 'game'. Use 'app' instead.
+
+## 1.7.1 - Jun 6, 2018
+
+* Made it possible to use `appbase_domain` with `https` URLs.
+
+* Fixed issue with undecorated splash window being unclosable if failures happen early in
+  initialization process. (#57)
+
+* Added support for transparent splash window. (#92)
+
+* Fixed problem with unpacked code resources (`ucode`) and `pack.gz` files. (#95)
+
+* Changed default Java version regex to support new Java 9+ version formats. (#93)
+
+* Ensure correct signature algorithm is used for each version of digest files. (#91)
+
+* Use more robust delete in all cases where Getdown needs to delete files. This should fix issues
+  with lingering files on Windows (where sometimes delete fails spuriously).
+
+## 1.7.0 - Dec 12, 2017
+
+* Fixed issue with `Digester` thread pool not being shutdown. (#89)
+
+* Fixed resource unpacking, which was broken by earlier change introducing resource installation
+  (downloading to `_new` files and then renaming into place). (#88)
+
+* The connect and read timeouts specified by system properties are now used for all the various
+  connections made by Getdown.
+
+* Proxy detection now uses a 5 second connect/read timeout, to avoid stalling for a long time in
+  certain problematic network conditions.
+
+* Getdown is now built against JDK 1.7 and requires JDK 1.7 (or newer) to run. Use the latest
+  Getdown 1.6.x release if you need to support Java 1.6.
+
+## 1.6.4 - Sep 17, 2017
+
+* `digest.txt` (and `digest2.txt`) computation now uses parallel jobs. Each resource to be verified
+  is a single job and the jobs are doled out to a thread pool with #CPUs threads. This allows large
+  builds to proceed faster as most dev machines have more than one core.
+
+* Resource verification is now performed in parallel (similar to the `digest.txt` computation, each
+  resource is a job farmed out to a thread pool). For large installations on multi-core machines,
+  this speeds up the verification phase of an installation or update.
+
+* Socket reads now have a 30 second default timeout. This can be changed by passing
+  `-Dread_timeout=N` (where N is seconds) to the JVM running Getdown.
+
+* Fixed issue with failing to install a downloaded and validated `_new` file.
+
+* Added support for "strict comments". In this mode, Getdown only treats `#` as starting a comment
+  if it appears in column zero. This allows `#` to occur on the right hand side of configuration
+  values (like in file names). To enable, put `strict_comments = true` in your `getdown.txt` file.
+
+## 1.6.3 - Apr 23, 2017
+
+* Fixed error parsing `cache_retention_days`. (#82)
+
+* Fixed error with new code cache. (9e23a426)
+
+## 1.6.2 - Feb 12, 2017
+
+* Fixed issue with installing local JVM, caused by new resource installation process. (#78)
+
+* Local JVM now uses absolute path to avoid issues with cwd.
+
+* Added `override_appbase` system property. This enables a Getdown app that normally talks to some
+  download server to be installed in such a way that it instead talks to some other download
+  server.
+
+## 1.6.1 - Feb 12, 2017
+
+* Fix issues with URL path encoding when downloading resources. (84af080b0)
+
+* Parsing `digest.txt` changed to allow `=` to appear in the filename. In `getdown.txt` we split on
+  the first `=` because `=` never appears in a key but may appear in a value. But in `digest.txt`
+  the format is `filename = hash` and `=` never appears in the hash but may appear in the filename,
+  so there we want to split on the _last_ `=` not the first.
+
+* Fixed bug with progress tracking and reporting. (256e0933)
+
+* Fix executable permissions on `jspawnhelper`. (#74)
+
+## 1.6 - Nov 5, 2016
+
+* This release and all those before it are considered ancient history. Check the commit history for
+  more details on what was in each of these releases.
+
+## 1.0 - Sep 21, 2010
+
+* The first Maven release of Getdown.
+
+## 0.1 - July 19, 2004
+
+* The first production use of Getdown (on https://www.puzzlepirates.com which is miraculously still
+  operational as of 2018 when this changelog was created).
+
+[migrating from 1.7 to 1.8]: https://github.com/threerings/getdown/wiki/Migrate17to18
diff --git a/getdown/src/getdown/LICENSE b/getdown/src/getdown/LICENSE
new file mode 100644 (file)
index 0000000..0d9b255
--- /dev/null
@@ -0,0 +1,24 @@
+Getdown - application installer, patcher and launcher
+
+Copyright (C) 2004-2016 Getdown authors
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGE.
diff --git a/getdown/src/getdown/README.md b/getdown/src/getdown/README.md
new file mode 100644 (file)
index 0000000..7059c61
--- /dev/null
@@ -0,0 +1,111 @@
+## What is it?
+
+Getdown (yes, it's the funky stuff) is a system for deploying Java applications to end-user
+computers, as well as keeping those applications up to date.
+
+It was designed as a replacement for [Java Web Start](https://docs.oracle.com/javase/8/docs/technotes/guides/javaws/)
+due to limitations in Java Web Start's architecture which are outlined in the
+[rationale](https://github.com/threerings/getdown/wiki/Rationale) section.
+
+Note: Getdown was designed *in 2004* as an alternative to Java Web Start, because of design choices
+made by JWS that were problematic to the use cases its authors had. It is _not_ a drop-in
+replacement for JWS, aimed to help the developers left in the lurch by the deprecation of JWS in
+Java 9. It may still be a viable alternative for developers looking to replace JWS, but don't
+expect to find feature parity with JWS.
+
+## How do I use it?
+
+A tutorial and more detailed specification are available from the [Documentation] page. Questions
+can be posted to the [OOO Libs Google group].
+
+Note that because one can not rely on users having a JRE installed, you must create a custom
+installer for each platform that you plan to support (Windows, macOS, Linux) that installs a JRE,
+the Getdown launcher jar file, a stub configuration file that identifies the URL at which your real
+app manifest is hosted, and whatever the appropiate "desktop integration" is that provides an icon
+the user can click on. We have some details on the
+[installers](https://github.com/threerings/getdown/wiki/Installers) documentation page, though it
+is unfortunately not very detailed.
+
+## How does it work?
+
+The main design and operation of Getdown is detailed on the
+[design](https://github.com/threerings/getdown/wiki/Design) page. You can also browse the
+[javadoc documentation] and [source code] if you're interested in implementation details.
+
+## Where can I see it in action?
+
+Getdown was originally written by developers at [OOO] for the deployment of their Java-based
+massively multiplayer games. Try out any of the following games to see it in action:
+
+  * [Puzzle Pirates](https://www.puzzlepirates.com/) - OOO
+  * [Spiral Knights](https://www.spiralknights.com/) - OOO
+
+Getdown is implemented in Java, and is designed to deploy and update JVM-based applications. While
+it would be technically feasible to use Getdown to deploy non-JVM-based applications, it is not
+currently supported and it is unlikely that the overhead of bundling a JVM just to run Getdown
+would be worth it if the JVM were not also being used to run the target application.
+
+## Release notes
+
+See [CHANGELOG.md](CHANGELOG.md) for release notes.
+
+## Obtaining Getdown
+
+Getdown will likely need to be integrated into your build. We have separate instructions for
+[build integration]. You can also download the individual jar files from Maven Central if needed.
+Getdown is comprised of three Maven artifacts (jar files), though you probably only need the first
+one:
+
+  * [getdown-launcher](http://repo2.maven.org/maven2/com/threerings/getdown/getdown-launcher)
+    contains minified (via Proguard) code that you actually run to update and launch your app. It
+    also contains the tools needed to build a Getdown app distribution.
+
+  * [getdown-core](http://repo2.maven.org/maven2/com/threerings/getdown/getdown-core) contains the
+    core logic for downloading, verifying, patching and launching an app as well as the core logic
+    for creating an app distribution. It does not contain any user interface code. You would only
+    use this artifact if you were planning to integrate Getdown directly into your app.
+
+  * [getdown-ant](http://repo2.maven.org/maven2/com/threerings/getdown/getdown-ant) contains an Ant
+    task for building a Getdown app distribution. See the [build integration] instructions for
+    details.
+
+You can also:
+
+  * [Check out the code](https://github.com/threerings/getdown) and build it yourself.
+  * Browse the [source code] online.
+  * View the [javadoc documentation] online.
+
+## JVM Version Requirements
+
+  * Getdown version 1.8.x requires Java 7 VM or newer.
+  * Getdown version 1.7.x requires Java 7 VM or newer.
+  * Getdown version 1.6.x requires Java 6 VM or newer.
+  * Getdown version 1.5 and earlier requires Java 5 VM or newer.
+
+## Migrating from Getdown 1.7 to Getdown 1.8
+
+See [this document](https://github.com/threerings/getdown/wiki/Migrating-from-1.7-to-1.8) on the
+changes needed to migrate from Getdown 1.7 to 1.8.
+
+## Building
+
+Getdown is built with Maven in the standard ways. Invoke the following commands, for fun and
+profit:
+
+```
+% mvn compile  # builds the classes
+% mvn test     # builds and runs the unit tests
+% mvn package  # builds and creates jar file
+% mvn install  # builds, jars and installs in your local Maven repository
+```
+
+## Discussion
+
+Feel free to pop over to the [OOO Libs Google Group] to ask questions and get (and give) answers.
+
+[Documentation]: https://github.com/threerings/getdown/wiki
+[OOO Libs Google group]: http://groups.google.com/group/ooo-libs
+[source code]: https://github.com/threerings/getdown/tree/master/src/main/java/com/threerings/getdown/launcher
+[javadoc documentation]: https://threerings.github.com/getdown/apidocs/
+[OOO]: https://en.wikipedia.org/wiki/Three_Rings_Design
+[build integration]: https://github.com/threerings/getdown/wiki/Build-Integration
diff --git a/getdown/src/getdown/ant/.project-MOVED b/getdown/src/getdown/ant/.project-MOVED
new file mode 100644 (file)
index 0000000..097cb89
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>getdown-ant</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.m2e.core.maven2Builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.m2e.core.maven2Nature</nature>
+       </natures>
+</projectDescription>
diff --git a/getdown/src/getdown/ant/.settings/org.eclipse.core.resources.prefs b/getdown/src/getdown/ant/.settings/org.eclipse.core.resources.prefs
new file mode 100644 (file)
index 0000000..e9441bb
--- /dev/null
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/getdown/src/getdown/ant/.settings/org.eclipse.jdt.core.prefs b/getdown/src/getdown/ant/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..54e5672
--- /dev/null
@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/getdown/src/getdown/ant/.settings/org.eclipse.m2e.core.prefs b/getdown/src/getdown/ant/.settings/org.eclipse.m2e.core.prefs
new file mode 100644 (file)
index 0000000..f897a7f
--- /dev/null
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/getdown/src/getdown/ant/pom.xml b/getdown/src/getdown/ant/pom.xml
new file mode 100644 (file)
index 0000000..6d6f8e9
--- /dev/null
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>com.threerings.getdown</groupId>
+    <artifactId>getdown</artifactId>
+    <version>1.8.3-1.1.8_FJVL</version>
+  </parent>
+
+  <artifactId>getdown-ant</artifactId>
+  <packaging>jar</packaging>
+  <name>Getdown Ant Task</name>
+  <description>An Ant task for building Getdown app distributions</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.threerings.getdown</groupId>
+      <artifactId>getdown-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.ant</groupId>
+      <artifactId>ant</artifactId>
+      <version>1.7.1</version>
+      <scope>provided</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/getdown/src/getdown/ant/src/main/java/com/threerings/getdown/tools/DigesterTask.java b/getdown/src/getdown/ant/src/main/java/com/threerings/getdown/tools/DigesterTask.java
new file mode 100644 (file)
index 0000000..48cc8d4
--- /dev/null
@@ -0,0 +1,94 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.tools;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.security.GeneralSecurityException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+import com.threerings.getdown.data.Digest;
+
+/**
+ * An ant task used to create a <code>digest.txt</code> for a Getdown
+ * application deployment.
+ */
+public class DigesterTask extends Task
+{
+    /**
+     * Sets the application directory.
+     */
+    public void setAppdir (File appdir)
+    {
+        _appdir = appdir;
+    }
+
+    /**
+     * Sets the digest signing keystore.
+     */
+    public void setKeystore (File path)
+    {
+        _storepath = path;
+    }
+
+    /**
+     * Sets the keystore decryption key.
+     */
+    public void setStorepass (String password)
+    {
+        _storepass = password;
+    }
+
+    /**
+     * Sets the private key alias.
+     */
+    public void setAlias (String alias)
+    {
+        _storealias = alias;
+    }
+
+    /**
+     * Performs the actual work of the task.
+     */
+    @Override
+    public void execute () throws BuildException
+    {
+        // make sure appdir is set
+        if (_appdir == null) {
+            throw new BuildException("Must specify the path to the application directory " +
+                                     "via the 'appdir' attribute.");
+        }
+
+        // make sure _storepass and _keyalias are set, if _storepath is set
+        if (_storepath != null && (_storepass == null || _storealias == null)) {
+            throw new BuildException(
+                    "Must specify both a keystore password and a private key alias.");
+        }
+
+        try {
+            Digester.createDigests(_appdir, _storepath, _storepass, _storealias);
+        } catch (IOException ioe) {
+            throw new BuildException("Error creating digest: " + ioe.getMessage(), ioe);
+        } catch (GeneralSecurityException gse) {
+            throw new BuildException("Error creating signature: " + gse.getMessage(), gse);
+        }
+    }
+
+    /** The application directory in which we're creating a digest file. */
+    protected File _appdir;
+
+    /** The path to the keystore we'll use to sign the digest file, if any. */
+    protected File _storepath;
+
+    /** The decryption key for the keystore. */
+    protected String _storepass;
+
+    /** The private key alias. */
+    protected String _storealias;
+}
diff --git a/getdown/src/getdown/bin/differ b/getdown/src/getdown/bin/differ
new file mode 100755 (executable)
index 0000000..f48ed89
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+# we'll magically try to match dist/getdown.jar or target/getdown-1.x-SNAPSHOT.jar
+java -classpath */getdown*.jar com.threerings.getdown.tools.Differ "$@"
diff --git a/getdown/src/getdown/bin/patcher b/getdown/src/getdown/bin/patcher
new file mode 100755 (executable)
index 0000000..e09f67d
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+# we'll magically try to match dist/getdown.jar or target/getdown-1.x-SNAPSHOT.jar
+java -classpath */getdown*.jar com.threerings.getdown.tools.Patcher "$@"
diff --git a/getdown/src/getdown/core/.project-MOVED b/getdown/src/getdown/core/.project-MOVED
new file mode 100644 (file)
index 0000000..177252f
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>getdown-core</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.m2e.core.maven2Builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.m2e.core.maven2Nature</nature>
+       </natures>
+</projectDescription>
diff --git a/getdown/src/getdown/core/.settings/org.eclipse.core.resources.prefs b/getdown/src/getdown/core/.settings/org.eclipse.core.resources.prefs
new file mode 100644 (file)
index 0000000..0a9bbb8
--- /dev/null
@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+encoding//src/it/java=UTF-8
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
+encoding/<project>=UTF-8
diff --git a/getdown/src/getdown/core/.settings/org.eclipse.jdt.core.prefs b/getdown/src/getdown/core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..54e5672
--- /dev/null
@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/getdown/src/getdown/core/.settings/org.eclipse.m2e.core.prefs b/getdown/src/getdown/core/.settings/org.eclipse.m2e.core.prefs
new file mode 100644 (file)
index 0000000..f897a7f
--- /dev/null
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/getdown/src/getdown/core/pom.xml b/getdown/src/getdown/core/pom.xml
new file mode 100644 (file)
index 0000000..51d3d5f
--- /dev/null
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>com.threerings.getdown</groupId>
+    <artifactId>getdown</artifactId>
+    <version>1.8.3-1.1.8_FJVL</version>
+  </parent>
+
+  <artifactId>getdown-core</artifactId>
+  <packaging>jar</packaging>
+  <name>Getdown Core</name>
+  <description>Core Getdown functionality</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.12</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <version>2.22.0</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <!-- By default, no host whitelist is added to the binary, so it can be used
+       to download and run applications from any server. To create a custom
+       Getdown build that can only talk to whitelisted servers, set this
+       property on the command line, e.g. -Dgetdown.host.whitelist=my.server.com
+       Wildcards can be used (*.mycompany.com) and multiple values can be
+       separated by commas (app1.foo.com,app2.bar.com,app3.baz.com). -->
+  <properties>
+    <getdown.host.whitelist>jalview.org,*.jalview.org</getdown.host.whitelist>
+    <connect_timeout>8</connect_timeout>
+    <read_timeout>15</read_timeout>
+    <allow_file_protocol>false</allow_file_protocol>
+  </properties>
+
+  <build>
+    <resources>
+      <resource> <!-- include the LICENSE file in the jar -->
+        <directory>..</directory>
+        <includes><include>LICENSE</include></includes>
+      </resource>
+    </resources>
+
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.5</version>
+        <executions>
+          <execution>
+            <id>add-test-source</id>
+            <phase>process-resources</phase>
+            <goals>
+              <goal>add-test-source</goal>
+            </goals>
+            <configuration>
+              <sources>
+                <source>src/it/java</source>
+              </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <version>1.8</version>
+        <executions>
+          <execution>
+            <id>gen-build</id>
+            <phase>generate-sources</phase>
+            <configuration>
+              <target>
+                <tstamp>
+                  <format property="getdown.build.time" pattern="yyyy-MM-dd HH:mm" />
+                </tstamp>
+                <copy file="${project.build.sourceDirectory}/com/threerings/getdown/data/Build.java.tmpl" tofile="${project.build.sourceDirectory}/com/threerings/getdown/data/Build.java" overwrite="true">
+                  <filterset>
+                    <filter token="build_time" value="${getdown.build.time}" />
+                    <filter token="build_version" value="${project.version}" />
+                    <filter token="host_whitelist" value="${getdown.host.whitelist}" />
+                    <filter token="connect_timeout" value="${connect_timeout}" />
+                    <filter token="read_timeout" value="${read_timeout}" />
+                    <filter token="allow_file_protocol" value="${allow_file_protocol}" />
+                  </filterset>
+                </copy>
+              </target>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-clean-plugin</artifactId>
+        <version>3.1.0</version>
+        <configuration>
+          <filesets>
+            <fileset>
+              <directory>${project.build.sourceDirectory}/</directory>
+              <includes>
+                <include>com/threerings/getdown/data/Build.java</include>
+              </includes>
+              <followSymlinks>false</followSymlinks>
+            </fileset>
+          </filesets>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+        <version>2.22.0</version>
+        <executions>
+          <execution>
+            <goals>
+              <goal>integration-test</goal>
+              <goal>verify</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <useFile>false</useFile>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/getdown/src/getdown/core/src/it/java/com/threerings/getdown/tests/DigesterIT.java b/getdown/src/getdown/core/src/it/java/com/threerings/getdown/tests/DigesterIT.java
new file mode 100644 (file)
index 0000000..52b4b5e
--- /dev/null
@@ -0,0 +1,54 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.tests;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.*;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.*;
+import static org.junit.Assert.*;
+
+import com.threerings.getdown.tools.Digester;
+
+public class DigesterIT {
+
+    @Test
+    public void testDigester () throws Exception {
+        Path appdir = Paths.get("src/it/resources/testapp");
+        Digester.createDigests(appdir.toFile(), null, null, null);
+
+        Path digest = appdir.resolve("digest.txt");
+        List<String> digestLines = Files.readAllLines(digest, StandardCharsets.UTF_8);
+        Files.delete(digest);
+
+        Path digest2 = appdir.resolve("digest2.txt");
+        List<String> digest2Lines = Files.readAllLines(digest2, StandardCharsets.UTF_8);
+        Files.delete(digest2);
+
+        assertEquals(Arrays.asList(
+            "getdown.txt = 779c74fb4b251e18faf6e240a0667964",
+            "testapp.jar = 404dafa55e78b25ec0e3a936357b1883",
+            "funny%test dir/some=file.txt = d8e8fca2dc0f896fd7cb4cb0031ba249",
+            "crazyhashfile#txt = f29d23fd5ab1781bd8d0760b3a516f16",
+            "foo.jar = 46ca4cc9079d9d019bb30cd21ebbc1ec",
+            "script.sh = f66e8ea25598e67e99c47d9b0b2a2cdf",
+            "digest.txt = f5561d85e4d80cc85883963897e58ff6"
+        ), digestLines);
+
+        assertEquals(Arrays.asList(
+            "getdown.txt = 4f0c657895c3c3a35fa55bf5951c64fa9b0694f8fc685af3f1d8635c639e066b",
+            "testapp.jar = c9cb1906afbf48f8654b416c3f831046bd3752a76137e5bf0a9af2f790bf48e0",
+            "funny%test dir/some=file.txt = f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2",
+            "crazyhashfile#txt = 6816889f922de38f145db215a28ad7c5e1badf7354b5cdab225a27486789fa3b",
+            "foo.jar = ea188b872e0496debcbe00aaadccccb12a8aa9b025bb62c130cd3d9b8540b062",
+            "script.sh = cca1c5c7628d9bf7533f655a9cfa6573d64afb8375f81960d1d832dc5135c988",
+            "digest2.txt = 70b442c9f56660561921da3368e1a206f05c379182fab3062750b7ddcf303407"
+        ), digest2Lines);
+    }
+}
diff --git a/getdown/src/getdown/core/src/it/resources/testapp/background.png b/getdown/src/getdown/core/src/it/resources/testapp/background.png
new file mode 100644 (file)
index 0000000..ff6a6ee
Binary files /dev/null and b/getdown/src/getdown/core/src/it/resources/testapp/background.png differ
diff --git a/getdown/src/getdown/core/src/it/resources/testapp/crazyhashfile#txt b/getdown/src/getdown/core/src/it/resources/testapp/crazyhashfile#txt
new file mode 100644 (file)
index 0000000..33bc373
--- /dev/null
@@ -0,0 +1 @@
+Hello crazy world.
diff --git a/getdown/src/getdown/core/src/it/resources/testapp/foo.jar b/getdown/src/getdown/core/src/it/resources/testapp/foo.jar
new file mode 100644 (file)
index 0000000..d040c01
Binary files /dev/null and b/getdown/src/getdown/core/src/it/resources/testapp/foo.jar differ
diff --git a/getdown/src/getdown/core/src/it/resources/testapp/funny%test dir/some=file.txt b/getdown/src/getdown/core/src/it/resources/testapp/funny%test dir/some=file.txt
new file mode 100644 (file)
index 0000000..9daeafb
--- /dev/null
@@ -0,0 +1 @@
+test
diff --git a/getdown/src/getdown/core/src/it/resources/testapp/getdown.txt b/getdown/src/getdown/core/src/it/resources/testapp/getdown.txt
new file mode 100644 (file)
index 0000000..3e0e538
--- /dev/null
@@ -0,0 +1,28 @@
+# where our app is hosted on the internets
+appbase = http://notused.com/testapp
+
+# the jar file that contains our code
+code = testapp.jar
+
+# the main entry point of our app
+class = com.threerings.testapp.TestApp
+
+# we pass the appdir to our app so that it can upgrade getdown
+apparg = %APPDIR%
+
+# test the %env% mechanism
+jvmarg = -Dusername=\%ENV.USER%
+
+strict_comments = true
+resource = funny%test dir/some=file.txt
+resource = crazyhashfile#txt
+uresource = foo.jar
+xresource = script.sh
+
+ui.name = Getdown Test App
+ui.background_image = background.png
+ui.progress = 17, 321, 458, 22
+ui.progress_bar = 336600
+ui.progress_text = FFFFFF
+ui.status = 57, 245, 373, 68
+ui.status_text = 000000
diff --git a/getdown/src/getdown/core/src/it/resources/testapp/script.sh b/getdown/src/getdown/core/src/it/resources/testapp/script.sh
new file mode 100644 (file)
index 0000000..e3a1aba
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "Hello world!"
diff --git a/getdown/src/getdown/core/src/it/resources/testapp/testapp.jar b/getdown/src/getdown/core/src/it/resources/testapp/testapp.jar
new file mode 100644 (file)
index 0000000..fe9de02
Binary files /dev/null and b/getdown/src/getdown/core/src/it/resources/testapp/testapp.jar differ
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/Log.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/Log.java
new file mode 100644 (file)
index 0000000..13b9956
--- /dev/null
@@ -0,0 +1,141 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.FieldPosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.logging.*;
+
+/**
+ * A placeholder class that contains a reference to the log object used by the Getdown code.
+ */
+public class Log
+{
+    public static class Shim {
+        /**
+         * Logs a debug message.
+         *
+         * @param message the message to be logged.
+         * @param args a list of key/value pairs and an optional final Throwable.
+         */
+        public void debug (Object message, Object... args) { doLog(0, message, args); }
+
+        /**
+         * Logs an info message.
+         *
+         * @param message the message to be logged.
+         * @param args a list of key/value pairs and an optional final Throwable.
+         */
+        public void info (Object message, Object... args) { doLog(1, message, args); }
+
+        /**
+         * Logs a warning message.
+         *
+         * @param message the message to be logged.
+         * @param args a list of key/value pairs and an optional final Throwable.
+         */
+        public void warning (Object message, Object... args) { doLog(2, message, args); }
+
+        /**
+         * Logs an error message.
+         *
+         * @param message the message to be logged.
+         * @param args a list of key/value pairs and an optional final Throwable.
+         */
+        public void error (Object message, Object... args) { doLog(3, message, args); }
+
+        protected void doLog (int levIdx, Object message, Object[] args) {
+            if (_impl.isLoggable(LEVELS[levIdx])) {
+                Throwable err = null;
+                int nn = args.length;
+                if (message instanceof Throwable) {
+                    err = (Throwable)message;
+                } else if (nn % 2 == 1 && (args[nn - 1] instanceof Throwable)) {
+                    err = (Throwable)args[--nn];
+                }
+                _impl.log(LEVELS[levIdx], format(message, args), err);
+            }
+        }
+
+        protected final Logger _impl = Logger.getLogger("com.threerings.getdown");
+    }
+
+    /** We dispatch our log messages through this logging shim. */
+    public static final Shim log = new Shim();
+
+    public static String format (Object message, Object... args) {
+        if (args.length < 2) return String.valueOf(message);
+        StringBuilder buf = new StringBuilder(String.valueOf(message));
+        if (buf.length() > 0) {
+            buf.append(' ');
+        }
+        buf.append('[');
+        for (int ii = 0; ii < args.length; ii += 2) {
+            if (ii > 0) {
+                buf.append(',').append(' ');
+            }
+            buf.append(args[ii]).append('=');
+            try {
+                buf.append(args[ii+1]);
+            } catch (Throwable t) {
+                buf.append("<toString() failure: ").append(t).append(">");
+            }
+        }
+        return buf.append(']').toString();
+    }
+
+    static {
+        Formatter formatter = new OneLineFormatter();
+        Logger logger = LogManager.getLogManager().getLogger("");
+        for (Handler handler : logger.getHandlers()) {
+            handler.setFormatter(formatter);
+        }
+    }
+
+    protected static class OneLineFormatter extends Formatter {
+        @Override public String format (LogRecord record) {
+            StringBuffer buf = new StringBuffer();
+
+            // append the timestamp
+            _date.setTime(record.getMillis());
+            _format.format(_date, buf, _fpos);
+
+            // append the log level
+            buf.append(" ");
+            buf.append(record.getLevel().getLocalizedName());
+            buf.append(" ");
+
+            // append the message itself
+            buf.append(formatMessage(record));
+            buf.append(System.lineSeparator());
+
+            // if an exception was also provided, append that
+            if (record.getThrown() != null) {
+                try {
+                    StringWriter sw = new StringWriter();
+                    PrintWriter pw = new PrintWriter(sw);
+                    record.getThrown().printStackTrace(pw);
+                    pw.close();
+                    buf.append(sw.toString());
+                } catch (Exception ex) {
+                    buf.append("Format failure:").append(ex);
+                }
+            }
+
+            return buf.toString();
+        }
+
+        protected Date _date = new Date();
+        protected SimpleDateFormat _format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss:SSS");
+        protected FieldPosition _fpos = new FieldPosition(SimpleDateFormat.DATE_FIELD);
+    }
+
+    protected static final String DATE_FORMAT = "{0,date} {0,time}";
+    protected static final Level[] LEVELS = {Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE};
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java
new file mode 100644 (file)
index 0000000..67ea645
--- /dev/null
@@ -0,0 +1,99 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.cache;
+
+import java.io.File;
+import com.threerings.getdown.util.FileUtil;
+
+/**
+ * Collects elements in the {@link ResourceCache cache} which became unused and deletes them
+ * afterwards.
+ */
+public class GarbageCollector
+{
+    /**
+     * Collect and delete the garbage in the cache.
+     */
+    public static void collect (File cacheDir, final long retentionPeriodMillis)
+    {
+        FileUtil.walkTree(cacheDir, new FileUtil.Visitor() {
+            @Override public void visit (File file) {
+                File cachedFile = getCachedFile(file);
+                File lastAccessedFile = getLastAccessedFile(file);
+                if (!cachedFile.exists() || !lastAccessedFile.exists()) {
+                    if (cachedFile.exists()) {
+                        FileUtil.deleteHarder(cachedFile);
+                    } else {
+                        FileUtil.deleteHarder(lastAccessedFile);
+                    }
+                } else if (shouldDelete(lastAccessedFile, retentionPeriodMillis)) {
+                    FileUtil.deleteHarder(lastAccessedFile);
+                    FileUtil.deleteHarder(cachedFile);
+                }
+
+                File folder = file.getParentFile();
+                if (folder != null) {
+                    String[] children = folder.list();
+                    if (children != null && children.length == 0) {
+                        FileUtil.deleteHarder(folder);
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * Collect and delete garbage in the native cache. It tries to find a jar file with a matching
+     * last modified file, and deletes the entire directory accordingly.
+     */
+    public static void collectNative (File cacheDir, final long retentionPeriodMillis)
+    {
+        File[] subdirs = cacheDir.listFiles();
+        if (subdirs != null) {
+            for (File dir : subdirs) {
+                if (dir.isDirectory()) {
+                    // Get all the native jars in the directory (there should only be one)
+                    for (File file : dir.listFiles()) {
+                        if (!file.getName().endsWith(".jar")) {
+                            continue;
+                        }
+                        File cachedFile = getCachedFile(file);
+                        File lastAccessedFile = getLastAccessedFile(file);
+                        if (!cachedFile.exists() || !lastAccessedFile.exists() ||
+                            shouldDelete(lastAccessedFile, retentionPeriodMillis)) {
+                            FileUtil.deleteDirHarder(dir);
+                        }
+                    }
+                } else {
+                    // @TODO There shouldn't be any loose files in native/ but if there are then
+                    // what? Delete them? file.delete();
+                }
+            }
+        }
+    }
+
+    private static boolean shouldDelete (File lastAccessedFile, long retentionMillis)
+    {
+        return System.currentTimeMillis() - lastAccessedFile.lastModified() > retentionMillis;
+    }
+
+    private static File getLastAccessedFile (File file)
+    {
+        return isLastAccessedFile(file) ? file : new File(
+            file.getParentFile(), file.getName() + ResourceCache.LAST_ACCESSED_FILE_SUFFIX);
+    }
+
+    private static boolean isLastAccessedFile (File file)
+    {
+        return file.getName().endsWith(ResourceCache.LAST_ACCESSED_FILE_SUFFIX);
+    }
+
+    private static File getCachedFile (File file)
+    {
+        return !isLastAccessedFile(file) ? file : new File(
+            file.getParentFile(), file.getName().substring(0, file.getName().lastIndexOf(".")));
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/ResourceCache.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/ResourceCache.java
new file mode 100644 (file)
index 0000000..0210e9a
--- /dev/null
@@ -0,0 +1,80 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.cache;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.threerings.getdown.util.FileUtil;
+
+/**
+ * Maintains a cache of code resources. The cache allows multiple application instances of different
+ * versions to open at the same time.
+ */
+public class ResourceCache
+{
+    public ResourceCache (File _cacheDir) throws IOException
+    {
+        this._cacheDir = _cacheDir;
+        createDirectoryIfNecessary(_cacheDir);
+    }
+
+    private void createDirectoryIfNecessary (File dir) throws IOException
+    {
+        if (!dir.exists() && !dir.mkdirs()) {
+            throw new IOException("unable to create directory: " + dir.getAbsolutePath());
+        }
+    }
+
+    /**
+     * Caches the given file under its {@code digest}.
+     * @param fileToCache file to cache.
+     * @param cacheSubdir the subdirectory of the cache directory in which to store the cached
+     * file. Usually either {@code digest} or a prefix of {@code digest}.
+     * @param digest a crypto digest of the cached files contents.
+     * @return the cached file.
+     */
+    public File cacheFile (File fileToCache, String cacheSubdir, String digest) throws IOException
+    {
+        File cacheLocation = new File(_cacheDir, cacheSubdir);
+        createDirectoryIfNecessary(cacheLocation);
+
+        File cachedFile = new File(cacheLocation, digest + getFileSuffix(fileToCache));
+        File lastAccessedFile = new File(
+                cacheLocation, cachedFile.getName() + LAST_ACCESSED_FILE_SUFFIX);
+
+        if (!cachedFile.exists()) {
+            createNewFile(cachedFile);
+            FileUtil.copy(fileToCache, cachedFile);
+        }
+
+        if (lastAccessedFile.exists()) {
+            lastAccessedFile.setLastModified(System.currentTimeMillis());
+        } else {
+            createNewFile(lastAccessedFile);
+        }
+
+        return cachedFile;
+    }
+
+    private void createNewFile (File fileToCreate) throws IOException
+    {
+        if (!fileToCreate.exists() && !fileToCreate.createNewFile()) {
+            throw new IOException("unable to create new file: " + fileToCreate.getAbsolutePath());
+        }
+    }
+
+    private String getFileSuffix (File fileToCache) {
+        String fileName = fileToCache.getName();
+        int index = fileName.lastIndexOf(".");
+
+        return index > -1 ? fileName.substring(index) : "";
+    }
+
+    private final File _cacheDir;
+
+    static final String LAST_ACCESSED_FILE_SUFFIX = ".lastAccessed";
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java
new file mode 100644 (file)
index 0000000..7c2a427
--- /dev/null
@@ -0,0 +1,2016 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.io.*;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.channels.OverlappingFileLockException;
+import java.security.*;
+import java.security.cert.Certificate;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.GZIPInputStream;
+
+import jalview.bin.MemorySetting;
+
+import com.threerings.getdown.util.*;
+// avoid ambiguity with java.util.Base64 which we can't use as it's 1.8+
+import com.threerings.getdown.util.Base64;
+
+import com.threerings.getdown.data.EnvConfig;
+import com.threerings.getdown.data.EnvConfig.Note;
+
+import static com.threerings.getdown.Log.log;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Parses and provide access to the information contained in the <code>getdown.txt</code>
+ * configuration file.
+ */
+public class Application
+{
+    /** The name of our configuration file. */
+    public static final String CONFIG_FILE = "getdown.txt";
+    
+    /** Dir where a backup config file might reside */ 
+    public static final String BACKUP_CONFIG_DIR = "install";
+
+    /** The name of our target version file. */
+    public static final String VERSION_FILE = "version.txt";
+
+    /** System properties that are prefixed with this string will be passed through to our
+     * application (minus this prefix). */
+    public static final String PROP_PASSTHROUGH_PREFIX = "app.";
+
+    /** Suffix used for control file signatures. */
+    public static final String SIGNATURE_SUFFIX = ".sig";
+
+    /** A special classname that means 'use -jar code.jar' instead of a classname. */
+    public static final String MANIFEST_CLASS = "manifest";
+
+    /** Used to communicate information about the UI displayed when updating the application. */
+    public static final class UpdateInterface
+    {
+        /**
+         * The major steps involved in updating, along with some arbitrary percentages
+         * assigned to them, to mark global progress.
+         */
+        public enum Step
+        {
+            //UPDATE_JAVA(10),
+            UPDATE_JAVA(20),
+            //VERIFY_METADATA(15, 65, 95),
+            VERIFY_METADATA(15, 45, 90),
+            DOWNLOAD(60),
+            PATCH(60),
+            //VERIFY_RESOURCES(70, 97),
+            VERIFY_RESOURCES(40, 90),
+            //REDOWNLOAD_RESOURCES(90),
+            REDOWNLOAD_RESOURCES(80),
+            //UNPACK(98),
+            UNPACK(95),
+            //LAUNCH(99);
+            LAUNCH(100);
+
+            /** What is the final percent value for this step? */
+            public final List<Integer> defaultPercents;
+
+            /** Enum constructor. */
+            Step (int... percents)
+            {
+                this.defaultPercents = intsToList(percents);
+            }
+        }
+
+        /** The human readable name of this application. */
+        public final String name;
+
+        /** A background color, just in case. */
+        public final int background;
+
+        /** Background image specifiers for `RotatingBackgrounds`. */
+        public final List<String> rotatingBackgrounds;
+
+        /** The error background image for `RotatingBackgrounds`. */
+        public final String errorBackground;
+
+        /** The paths (relative to the appdir) of images for the window icon. */
+        public final List<String> iconImages;
+
+        /** The path (relative to the appdir) to a single background image to appear first. */
+        public final String instantBackgroundImage;
+
+        /** The path (relative to the appdir) to a single background image. */
+        public final String backgroundImage;
+
+        /** The path (relative to the appdir) to the progress bar image. */
+        public final String progressImage;
+
+        /** The dimensions of the progress bar. */
+        public final Rectangle progress;
+
+        /** The color of the progress text. */
+        public final int progressText;
+
+        /** The color of the progress bar. */
+        public final int progressBar;
+
+        /** The dimensions of the status display. */
+        public final Rectangle status;
+
+        /** The color of the status text. */
+        public final int statusText;
+
+        /** The color of the text shadow. */
+        public final int textShadow;
+
+        /** Where to point the user for help with install errors. */
+        public final String installError;
+
+        /** The dimensions of the patch notes button. */
+        public final Rectangle patchNotes;
+
+        /** The patch notes URL. */
+        public final String patchNotesUrl;
+
+        /** Whether window decorations are hidden for the UI. */
+        public final boolean hideDecorations;
+
+        /** Whether progress text should be hidden or not. */
+        public final boolean hideProgressText;
+
+        /** Whether the splash screen should update non-asynchronously before being shown. */
+        public final boolean progressSync;
+
+        /** Whether the splash screen should update non-asynchronously after being shown. */
+        public final boolean progressSyncAfterShown;
+
+        /** Whether the splash screen should retain focus. */
+        public final boolean keepOnTop;
+
+        /** Whether to display the appbase. */
+        public final boolean displayAppbase;
+
+        /** Whether to display the version. */
+        public final boolean displayVersion;
+
+        /** The minimum number of seconds to display the GUI. This is to prevent the GUI from
+          * flashing up on the screen and immediately disappearing, which can be confusing to the
+          * user. */
+        public final int minShowSeconds;
+
+        /** The global percentages for each step. A step may have more than one, and
+         * the lowest reasonable one is used if a step is revisited. */
+        public final Map<Step, List<Integer>> stepPercentages;
+
+        /** Generates a string representation of this instance. */
+        @Override
+        public String toString ()
+        {
+            return "[name=" + name + ", bg=" + background + ", bg=" + backgroundImage + ", instant_bg=" + instantBackgroundImage +
+                ", pi=" + progressImage + ", prect=" + progress + ", pt=" + progressText +
+                ", pb=" + progressBar + ", srect=" + status + ", st=" + statusText +
+                ", shadow=" + textShadow + ", err=" + installError + ", nrect=" + patchNotes +
+                ", notes=" + patchNotesUrl + ", stepPercentages=" + stepPercentages +
+                ", hideProgressText=" + hideProgressText + ", keepOnTop=" + keepOnTop + ", progressSync=" + progressSync +
+                ", progressSyncAfterShown=" + progressSyncAfterShown + ", minShow=" + minShowSeconds +
+                ", displayAppbase=" + displayAppbase + ", displayVersion=" + displayVersion + "]";
+        }
+
+        public UpdateInterface (Config config)
+        {
+            this.name = config.getString("ui.name");
+            this.progress = config.getRect("ui.progress", new Rectangle(5, 5, 300, 15));
+            this.progressText = config.getColor("ui.progress_text", Color.BLACK);
+            this.hideProgressText = config.getBoolean("ui.hide_progress_text");
+            this.progressSync = config.getBoolean("ui.progress_sync_before_shown");
+            this.progressSyncAfterShown = config.getBoolean("ui.progress_sync_after_shown");
+            this.keepOnTop =  config.getBoolean("ui.keep_on_top");
+            this.displayAppbase =  config.getBoolean("ui.display_appbase");
+            this.displayVersion =  config.getBoolean("ui.display_version");
+            this.minShowSeconds = config.getInt("ui.min_show_seconds", 5);
+            this.progressBar = config.getColor("ui.progress_bar", 0x6699CC);
+            this.status = config.getRect("ui.status", new Rectangle(5, 25, 500, 100));
+            this.statusText = config.getColor("ui.status_text", Color.BLACK);
+            this.textShadow = config.getColor("ui.text_shadow", Color.CLEAR);
+            this.hideDecorations = config.getBoolean("ui.hide_decorations");
+            this.backgroundImage = config.getString("ui.background_image");
+            this.instantBackgroundImage = config.getString("ui.instant_background_image");
+            // default to black or white bg color, depending on the brightness of the progressText
+            int defaultBackground = (0.5f < Color.brightness(this.progressText)) ?
+                Color.BLACK : Color.WHITE;
+            this.background = config.getColor("ui.background", defaultBackground);
+            this.progressImage = config.getString("ui.progress_image");
+            this.rotatingBackgrounds = stringsToList(
+                config.getMultiValue("ui.rotating_background"));
+            this.iconImages = stringsToList(config.getMultiValue("ui.icon"));
+            this.errorBackground = config.getString("ui.error_background");
+
+            // On an installation error, where do we point the user.
+            String installError = config.getUrl("ui.install_error", null);
+            this.installError = (installError == null) ?
+                "m.default_install_error" : MessageUtil.taint(installError);
+
+            // the patch notes bits
+            this.patchNotes = config.getRect("ui.patch_notes", new Rectangle(5, 50, 112, 26));
+            this.patchNotesUrl = config.getUrl("ui.patch_notes_url", null);
+
+            // step progress percentage (defaults and then customized values)
+            EnumMap<Step, List<Integer>> stepPercentages = new EnumMap<>(Step.class);
+            for (Step step : Step.values()) {
+                stepPercentages.put(step, step.defaultPercents);
+            }
+            for (UpdateInterface.Step step : UpdateInterface.Step.values()) {
+                String spec = config.getString("ui.percents." + step.name());
+                if (spec != null) {
+                    try {
+                        stepPercentages.put(step, intsToList(StringUtil.parseIntArray(spec)));
+                    } catch (Exception e) {
+                        log.warning("Failed to parse percentages for " + step + ": " + spec);
+                    }
+                }
+            }
+            this.stepPercentages = Collections.unmodifiableMap(stepPercentages);
+        }
+    }
+
+    /**
+     * Used by {@link #verifyMetadata} to communicate status in circumstances where it needs to
+     * take network actions.
+     */
+    public static interface StatusDisplay
+    {
+        /** Requests that the specified status message be displayed. */
+        public void updateStatus (String message);
+    }
+
+    /**
+     * Contains metadata for an auxiliary resource group.
+     */
+    public static class AuxGroup {
+        public final String name;
+        public final List<Resource> codes;
+        public final List<Resource> rsrcs;
+
+        public AuxGroup (String name, List<Resource> codes, List<Resource> rsrcs) {
+            this.name = name;
+            this.codes = Collections.unmodifiableList(codes);
+            this.rsrcs = Collections.unmodifiableList(rsrcs);
+        }
+    }
+
+    /** The proxy that should be used to do HTTP downloads. This must be configured prior to using
+      * the application instance. Yes this is a public mutable field, no I'm not going to create a
+      * getter and setter just to pretend like that's not the case. */
+    public Proxy proxy = Proxy.NO_PROXY;
+
+    /**
+     * Creates an application instance which records the location of the <code>getdown.txt</code>
+     * configuration file from the supplied application directory.
+     *
+     */
+    public Application (EnvConfig envc) {
+       _envc = envc;
+       _config = getLocalPath(envc.appDir, CONFIG_FILE);
+       _backupConfig = getLocalPath(envc.appDir, BACKUP_CONFIG_DIR+File.separator+CONFIG_FILE);
+    }
+
+    /**
+     * Returns the configured application directory.
+     */
+    public File getAppDir () {
+       return _envc.appDir;
+    }
+
+    /**
+     * Returns whether the application should cache code resources prior to launching the
+     * application.
+     */
+    public boolean useCodeCache ()
+    {
+       return _useCodeCache;
+    }
+
+    /**
+     * Returns the number of days a cached code resource is allowed to stay unused before it
+     * becomes eligible for deletion.
+     */
+    public int getCodeCacheRetentionDays ()
+    {
+       return _codeCacheRetentionDays;
+    }
+
+    /**
+     * Returns the configured maximum concurrent downloads. Used to cap simultaneous downloads of
+     * app files from its hosting server.
+     */
+    public int maxConcurrentDownloads () {
+       return _maxConcDownloads;
+    }
+
+    /**
+     * Returns a resource that refers to the application configuration file itself.
+     */
+    public Resource getConfigResource ()
+    {
+       try {
+               return createResource(CONFIG_FILE, Resource.NORMAL);
+       } catch (Exception e) {
+               throw new RuntimeException("Invalid appbase '" + _vappbase + "'.", e);
+       }
+    }
+
+    /**
+     * Returns a list of the code {@link Resource} objects used by this application.
+     */
+    public List<Resource> getCodeResources ()
+    {
+       return _codes;
+    }
+
+    /**
+     * Returns a list of the non-code {@link Resource} objects used by this application.
+     */
+    public List<Resource> getResources ()
+    {
+        return _resources;
+    }
+
+    /**
+     * Returns the digest of the given {@code resource}.
+     */
+    public String getDigest (Resource resource)
+    {
+        return _digest.getDigest(resource);
+    }
+
+    /**
+     * Returns a list of all the active {@link Resource} objects used by this application (code and
+     * non-code).
+     */
+    public List<Resource> getAllActiveResources ()
+    {
+        List<Resource> allResources = new ArrayList<>();
+        allResources.addAll(getActiveCodeResources());
+        allResources.addAll(getActiveResources());
+        return allResources;
+    }
+
+    /**
+     * Returns the auxiliary resource group with the specified name, or null.
+     */
+    public AuxGroup getAuxGroup (String name)
+    {
+        return _auxgroups.get(name);
+    }
+
+    /**
+     * Returns the set of all auxiliary resource groups defined by the application. An auxiliary
+     * resource group is a collection of resource files that are not downloaded unless a group
+     * token file is present in the application directory.
+     */
+    public Iterable<AuxGroup> getAuxGroups ()
+    {
+        return _auxgroups.values();
+    }
+
+    /**
+     * Returns true if the specified auxgroup has been "activated", false if not. Non-activated
+     * groups should be ignored, activated groups should be downloaded and patched along with the
+     * main resources.
+     */
+    public boolean isAuxGroupActive (String auxgroup)
+    {
+        Boolean active = _auxactive.get(auxgroup);
+        if (active == null) {
+            // TODO: compare the contents with the MD5 hash of the auxgroup name and the client's
+            // machine ident
+            active = getLocalPath(auxgroup + ".dat").exists();
+            _auxactive.put(auxgroup, active);
+        }
+        return active;
+    }
+
+    /**
+     * Returns all main code resources and all code resources from active auxiliary resource groups.
+     */
+    public List<Resource> getActiveCodeResources ()
+    {
+        ArrayList<Resource> codes = new ArrayList<>();
+        codes.addAll(getCodeResources());
+        for (AuxGroup aux : getAuxGroups()) {
+            if (isAuxGroupActive(aux.name)) {
+                codes.addAll(aux.codes);
+            }
+        }
+        return codes;
+    }
+
+    /**
+     * Returns all resources indicated to contain native library files (.dll, .so, etc.).
+     */
+    public List<Resource> getNativeResources ()
+    {
+        List<Resource> natives = new ArrayList<>();
+        for (Resource resource: _resources) {
+            if (resource.isNative()) {
+                natives.add(resource);
+            }
+        }
+        return natives;
+    }
+
+    /**
+     * Returns all non-code resources and all resources from active auxiliary resource groups.
+     */
+    public List<Resource> getActiveResources ()
+    {
+        ArrayList<Resource> rsrcs = new ArrayList<>();
+        rsrcs.addAll(getResources());
+        for (AuxGroup aux : getAuxGroups()) {
+            if (isAuxGroupActive(aux.name)) {
+                rsrcs.addAll(aux.rsrcs);
+            }
+        }
+        return rsrcs;
+    }
+
+    /**
+     * Returns a resource that can be used to download a patch file that will bring this
+     * application from its current version to the target version.
+     *
+     * @param auxgroup the auxiliary resource group for which a patch resource is desired or null
+     * for the main application patch resource.
+     */
+    public Resource getPatchResource (String auxgroup)
+    {
+        if (_targetVersion <= _version) {
+            log.warning("Requested patch resource for up-to-date or non-versioned application",
+                "cvers", _version, "tvers", _targetVersion);
+            return null;
+        }
+
+        String infix = (auxgroup == null) ? "" : ("-" + auxgroup);
+        String pfile = "patch" + infix + _version + ".dat";
+        try {
+            URL remote = new URL(createVAppBase(_targetVersion), encodePath(pfile));
+            return new Resource(pfile, remote, getLocalPath(pfile), Resource.NORMAL);
+        } catch (Exception e) {
+            log.warning("Failed to create patch resource path",
+                "pfile", pfile, "appbase", _appbase, "tvers", _targetVersion, "error", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns a resource for a zip file containing a Java VM that can be downloaded to use in
+     * place of the installed VM (in the case where the VM that launched Getdown does not meet the
+     * application's version requirements) or null if no VM is available for this platform.
+     */
+    public Resource getJavaVMResource ()
+    {
+        if (StringUtil.isBlank(_javaLocation)) {
+            return null;
+        }
+
+        String extension = (_javaLocation.endsWith(".tgz"))?".tgz":".jar";
+        String vmfile = LaunchUtil.LOCAL_JAVA_DIR + extension;
+               log.info("vmfile is '"+vmfile+"'");
+               System.out.println("vmfile is '"+vmfile+"'");
+        try {
+            URL remote = new URL(createVAppBase(_targetVersion), encodePath(_javaLocation));
+            log.info("Attempting to fetch jvm at "+remote.toString());
+            System.out.println("Attempting to fetch jvm at "+remote.toString());
+            return new Resource(vmfile, remote, getLocalPath(vmfile),
+                                EnumSet.of(Resource.Attr.UNPACK, Resource.Attr.CLEAN));
+        } catch (Exception e) {
+            log.warning("Failed to create VM resource", "vmfile", vmfile, "appbase", _appbase,
+                "tvers", _targetVersion, "javaloc", _javaLocation, "error", e);
+            System.out.println("Failed to create VM resource: vmfile="+vmfile+", appbase="+_appbase+
+                ", tvers="+_targetVersion+", javaloc="+_javaLocation+", error="+e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns a resource that can be used to download an archive containing all files belonging to
+     * the application.
+     */
+    public Resource getFullResource ()
+    {
+        String file = "full";
+        try {
+            URL remote = new URL(createVAppBase(_targetVersion), encodePath(file));
+            return new Resource(file, remote, getLocalPath(file), Resource.NORMAL);
+        } catch (Exception e) {
+            log.warning("Failed to create full resource path",
+                "file", file, "appbase", _appbase, "tvers", _targetVersion, "error", e);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the URL to use to report an initial download event. Returns null if no tracking
+     * start URL was configured for this application.
+     *
+     * @param event the event to be reported: start, jvm_start, jvm_complete, complete.
+     */
+    public URL getTrackingURL (String event)
+    {
+        try {
+            String suffix = _trackingURLSuffix == null ? "" : _trackingURLSuffix;
+            String ga = getGATrackingCode();
+            return _trackingURL == null ? null :
+                HostWhitelist.verify(new URL(_trackingURL + encodePath(event + suffix + ga)));
+        } catch (MalformedURLException mue) {
+            log.warning("Invalid tracking URL", "path", _trackingURL, "event", event, "error", mue);
+            return null;
+        }
+    }
+
+    /**
+     * Returns the URL to request to report that we have reached the specified percentage of our
+     * initial download. Returns null if no tracking request was configured for the specified
+     * percentage.
+     */
+    public URL getTrackingProgressURL (int percent)
+    {
+        if (_trackingPcts == null || !_trackingPcts.contains(percent)) {
+            return null;
+        }
+        return getTrackingURL("pct" + percent);
+    }
+
+    /**
+     * Returns the name of our tracking cookie or null if it was not set.
+     */
+    public String getTrackingCookieName ()
+    {
+        return _trackingCookieName;
+    }
+
+    /**
+     * Returns the name of our tracking cookie system property or null if it was not set.
+     */
+    public String getTrackingCookieProperty ()
+    {
+        return _trackingCookieProperty;
+    }
+
+    /**
+     * Instructs the application to parse its {@code getdown.txt} configuration and prepare itself
+     * for operation. The application base URL will be parsed first so that if there are errors
+     * discovered later, the caller can use the application base to download a new {@code
+     * getdown.txt} file and try again.
+     *
+     * @return a {@code Config} instance that contains information from the config file.
+     *
+     * @exception IOException thrown if there is an error reading the file or an error encountered
+     * during its parsing.
+     */
+    public Config init (boolean checkPlatform)
+        throws IOException
+    {
+        Config config = null;
+        File cfgfile = _config;
+        Config.ParseOpts opts = Config.createOpts(checkPlatform);
+        try {
+            // if we have a configuration file, read the data from it
+            if (cfgfile.exists()) {
+                config = Config.parseConfig(_config, opts);
+            }
+            // otherwise, try reading data from our backup config file; thanks to funny windows
+            // bullshit, we have to do this backup file fiddling in case we got screwed while
+            // updating getdown.txt during normal operation
+            else if ((cfgfile = getLocalPath(CONFIG_FILE + "_old")).exists()) {
+                config = Config.parseConfig(cfgfile, opts);
+            }
+            // otherwise, issue a warning that we found no getdown file
+            else {
+                log.info("Found no getdown.txt file", "appdir", getAppDir());
+            }
+        } catch (Exception e) {
+            log.warning("Failure reading config file", "file", _config, e);
+        }
+        if (config == null || config.getString("appbase") == null || config.getString("appbase").isEmpty()) {
+               try {
+                       Config backupConfig = Config.parseConfig(_backupConfig, opts);
+                       config = backupConfig;
+                       log.warning("Using backup config file", "appdir", getAppDir(), "backupConfig", _backupConfig.getAbsoluteFile());
+               } catch (Exception e) {
+                       log.warning("Failure reading backup config file", "file", _backupConfig, e);
+               }
+        }
+        
+        // see if there's an override config from locator file
+        Config locatorConfig = createLocatorConfig(opts);
+        
+        // merge the locator file config into config (or replace config with)
+        if (locatorConfig != null) {
+          if (config == null || locatorConfig.getBoolean(LOCATOR_FILE_EXTENSION+"_replace")) {
+            config = locatorConfig;
+          } else {
+            config.mergeConfig(locatorConfig, locatorConfig.getBoolean(LOCATOR_FILE_EXTENSION+"_merge"));
+          }
+        }
+
+        // if we failed to read our config file, check for an appbase specified via a system
+        // property; we can use that to bootstrap ourselves back into operation
+        if (config == null) {
+            String appbase = _envc.appBase;
+            log.info("Using 'appbase' from bootstrap config", "appbase", appbase);
+            Map<String, Object> cdata = new HashMap<>();
+            cdata.put("appbase", appbase);
+            config = new Config(cdata);
+        }
+
+        // first determine our application base, this way if anything goes wrong later in the
+        // process, our caller can use the appbase to download a new configuration file
+        _appbase = config.getString("appbase");
+        
+        // see if locatorConfig override
+        if (locatorConfig != null && !StringUtil.isBlank(locatorConfig.getString("appbase"))) {
+          _appbase = locatorConfig.getString("appbase");
+        }
+        
+        if (_appbase == null) {
+            throw new RuntimeException("m.missing_appbase");
+        }
+
+        // check if we're overriding the domain in the appbase
+        _appbase = SysProps.overrideAppbase(_appbase);
+
+        // make sure there's a trailing slash
+        if (!_appbase.endsWith("/")) {
+            _appbase = _appbase + "/";
+        }
+
+        // extract our version information
+        _version = config.getLong("version", -1L);
+
+        // if we are a versioned deployment, create a versioned appbase
+        try {
+            _vappbase = createVAppBase(_version);
+        } catch (MalformedURLException mue) {
+            String err = MessageUtil.tcompose("m.invalid_appbase", _appbase);
+            throw (IOException) new IOException(err).initCause(mue);
+        }
+
+        // check for a latest config URL
+        String latest = config.getString("latest");
+        if (latest != null) {
+            if (latest.startsWith(_appbase)) {
+                latest = _appbase + latest.substring(_appbase.length());
+            } else {
+                latest = SysProps.replaceDomain(latest);
+            }
+            try {
+                _latest = HostWhitelist.verify(new URL(latest));
+            } catch (MalformedURLException mue) {
+                log.warning("Invalid URL for latest attribute.", mue);
+            }
+        }
+
+        String appPrefix = _envc.appId == null ? "" : (_envc.appId + ".");
+
+        // determine our application class name (use app-specific class _if_ one is provided)
+        _class = config.getString("class");
+        if (appPrefix.length() > 0) {
+            _class = config.getString(appPrefix + "class", _class);
+        }
+        if (_class == null) {
+            throw new IOException("m.missing_class");
+        }
+
+        // determine whether we want strict comments
+        _strictComments = config.getBoolean("strict_comments");
+
+        // check to see if we're using a custom java.version property and regex
+        _javaVersionProp = config.getString("java_version_prop", _javaVersionProp);
+        _javaVersionRegex = config.getString("java_version_regex", _javaVersionRegex);
+
+        // check to see if we require a particular JVM version and have a supplied JVM
+        _javaMinVersion = config.getLong("java_version", _javaMinVersion);
+        // we support java_min_version as an alias of java_version; it better expresses the check
+        // that's going on and better mirrors java_max_version
+        _javaMinVersion = config.getLong("java_min_version", _javaMinVersion);
+        // check to see if we require a particular max JVM version and have a supplied JVM
+        _javaMaxVersion = config.getLong("java_max_version", _javaMaxVersion);
+        // check to see if we require a particular JVM version and have a supplied JVM
+        _javaExactVersionRequired = config.getBoolean("java_exact_version_required");
+
+        // this is a little weird, but when we're run from the digester, we see a String[] which
+        // contains java locations for all platforms which we can't grok, but the digester doesn't
+        // need to know about that; when we're run in a real application there will be only one!
+        Object javaloc = config.getRaw("java_location");
+        if (javaloc instanceof String) {
+            _javaLocation = (String)javaloc;
+        }
+
+        // determine whether we have any tracking configuration
+        _trackingURL = config.getString("tracking_url");
+
+        // check for tracking progress percent configuration
+        String trackPcts = config.getString("tracking_percents");
+        if (!StringUtil.isBlank(trackPcts)) {
+            _trackingPcts = new HashSet<>();
+            for (int pct : StringUtil.parseIntArray(trackPcts)) {
+                _trackingPcts.add(pct);
+            }
+        } else if (!StringUtil.isBlank(_trackingURL)) {
+            _trackingPcts = new HashSet<>();
+            _trackingPcts.add(50);
+        }
+
+        // Check for tracking cookie configuration
+        _trackingCookieName = config.getString("tracking_cookie_name");
+        _trackingCookieProperty = config.getString("tracking_cookie_property");
+
+        // Some app may need an extra suffix added to the tracking URL
+        _trackingURLSuffix = config.getString("tracking_url_suffix");
+
+        // Some app may need to generate google analytics code
+        _trackingGAHash = config.getString("tracking_ga_hash");
+
+        // clear our arrays as we may be reinitializing
+        _codes.clear();
+        _resources.clear();
+        _auxgroups.clear();
+        _jvmargs.clear();
+        _appargs.clear();
+        _txtJvmArgs.clear();
+
+        // parse our code resources
+        if (config.getMultiValue("code") == null &&
+            config.getMultiValue("ucode") == null) {
+            throw new IOException("m.missing_code");
+        }
+        parseResources(config, "code", Resource.NORMAL, _codes);
+        parseResources(config, "ucode", Resource.UNPACK, _codes);
+
+        // parse our non-code resources
+        parseResources(config, "resource", Resource.NORMAL, _resources);
+        parseResources(config, "uresource", Resource.UNPACK, _resources);
+        parseResources(config, "xresource", Resource.EXEC, _resources);
+        parseResources(config, "presource", Resource.PRELOAD, _resources);
+        parseResources(config, "nresource", Resource.NATIVE, _resources);
+
+        // parse our auxiliary resource groups
+        for (String auxgroup : config.getList("auxgroups")) {
+            ArrayList<Resource> codes = new ArrayList<>();
+            parseResources(config, auxgroup + ".code", Resource.NORMAL, codes);
+            parseResources(config, auxgroup + ".ucode", Resource.UNPACK, codes);
+            ArrayList<Resource> rsrcs = new ArrayList<>();
+            parseResources(config, auxgroup + ".resource", Resource.NORMAL, rsrcs);
+            parseResources(config, auxgroup + ".xresource", Resource.EXEC, rsrcs);
+            parseResources(config, auxgroup + ".uresource", Resource.UNPACK, rsrcs);
+            parseResources(config, auxgroup + ".presource", Resource.PRELOAD, rsrcs);
+            parseResources(config, auxgroup + ".nresource", Resource.NATIVE, rsrcs);
+            _auxgroups.put(auxgroup, new AuxGroup(auxgroup, codes, rsrcs));
+        }
+
+        // transfer our JVM arguments (we include both "global" args and app_id-prefixed args)
+        String[] jvmargs = config.getMultiValue("jvmarg");
+        addAll(jvmargs, _jvmargs);
+        if (appPrefix.length() > 0) {
+            jvmargs = config.getMultiValue(appPrefix + "jvmarg");
+            addAll(jvmargs, _jvmargs);
+        }
+
+        // see if a percentage of physical memory option exists
+        int jvmmempc = config.getInt("jvmmempc", -1);
+        // app_id prefixed setting overrides
+        if (appPrefix.length() > 0) {
+            jvmmempc = config.getInt(appPrefix + "jvmmempc", jvmmempc);
+        }
+        if (0 <= jvmmempc && jvmmempc <= 100) {
+          
+          long maxMemLong = -1;
+
+          try
+          {
+            maxMemLong = MemorySetting.memPercent(jvmmempc);
+          } catch (Exception e)
+          {
+            e.printStackTrace();
+          } catch (Throwable t)
+          {
+            t.printStackTrace();
+          }
+
+          if (maxMemLong > 0)
+          {
+            
+            String[] maxMemHeapArg = new String[]{"-Xmx"+Long.toString(maxMemLong)};
+            // remove other max heap size arg
+            ARG: for (int i = 0; i < _jvmargs.size(); i++) {
+              if (_jvmargs.get(i) instanceof java.lang.String && _jvmargs.get(i).startsWith("-Xmx")) {
+                _jvmargs.remove(i);
+                break ARG;
+              }
+            }
+            addAll(maxMemHeapArg, _jvmargs);
+            log.info("Max memory set", "maxMemHeapArg", maxMemHeapArg[0]);
+            
+          }
+
+        } else if (jvmmempc != -1) {
+          log.warning("'jvmmempc' value must be in range 0 to 100 (read as '"+Integer.toString(jvmmempc)+"')");
+        }
+
+        // get the set of optimum JVM arguments
+        _optimumJvmArgs = config.getMultiValue("optimum_jvmarg");
+
+        // transfer our application arguments
+        String[] appargs = config.getMultiValue(appPrefix + "apparg");
+        addAll(appargs, _appargs);
+
+        // add the launch specific application arguments
+        _appargs.addAll(_envc.appArgs);
+        
+        // look for custom arguments
+        fillAssignmentListFromPairs("extra.txt", _txtJvmArgs);
+
+        // determine whether we want to allow offline operation (defaults to false)
+        _allowOffline = config.getBoolean("allow_offline");
+
+        // look for a debug.txt file which causes us to run in java.exe on Windows so that we can
+        // obtain a thread dump of the running JVM
+        _windebug = getLocalPath("debug.txt").exists();
+
+        // whether to cache code resources and launch from cache
+        _useCodeCache = config.getBoolean("use_code_cache");
+        _codeCacheRetentionDays = config.getInt("code_cache_retention_days", 7);
+
+        // maximum simultaneous downloads
+        _maxConcDownloads = Math.max(1, config.getInt("max_concurrent_downloads",
+                                                      SysProps.threadPoolSize()));
+
+        // extract some info used to configure our child process on macOS
+        _dockName = config.getString("ui.name");
+        _dockIconPath = config.getString("ui.mac_dock_icon", "../desktop.icns");
+
+        return config;
+    }
+
+    /**
+     * Adds strings of the form pair0=pair1 to collector for each pair parsed out of pairLocation.
+     */
+    protected void fillAssignmentListFromPairs (String pairLocation, List<String> collector)
+    {
+        File pairFile = getLocalPath(pairLocation);
+        if (pairFile.exists()) {
+            try {
+                List<String[]> args = Config.parsePairs(pairFile, Config.createOpts(false));
+                for (String[] pair : args) {
+                    if (pair[1].length() == 0) {
+                        collector.add(pair[0]);
+                    } else {
+                        collector.add(pair[0] + "=" + pair[1]);
+                    }
+                }
+            } catch (Throwable t) {
+                log.warning("Failed to parse '" + pairFile + "': " + t);
+            }
+        }
+    }
+
+    /**
+     * Returns a URL from which the specified path can be fetched. Our application base URL is
+     * properly versioned and combined with the supplied path.
+     */
+    public URL getRemoteURL (String path)
+        throws MalformedURLException
+    {
+        return new URL(_vappbase, encodePath(path));
+    }
+
+    /**
+     * Returns the local path to the specified resource.
+     */
+    public File getLocalPath (String path)
+    {
+        return getLocalPath(getAppDir(), path);
+    }
+
+    /**
+     * Returns true if we either have no version requirement, are running in a JVM that meets our
+     * version requirements or have what appears to be a version of the JVM that meets our
+     * requirements.
+     */
+    public boolean haveValidJavaVersion ()
+    {
+        // if we're doing no version checking, then yay!
+        if (_javaMinVersion == 0 && _javaMaxVersion == 0) return true;
+
+        try {
+            // parse the version out of the java.version (or custom) system property
+            long version = SysProps.parseJavaVersion(_javaVersionProp, _javaVersionRegex);
+
+            log.info("Checking Java version", "current", version,
+                     "wantMin", _javaMinVersion, "wantMax", _javaMaxVersion);
+
+            // if we have an unpacked VM, check the 'release' file for its version
+            Resource vmjar = getJavaVMResource();
+            if (vmjar != null && vmjar.isMarkedValid()) {
+                File vmdir = new File(getAppDir(), LaunchUtil.LOCAL_JAVA_DIR);
+                File relfile = new File(vmdir, "release");
+                if (!relfile.exists()) {
+                    log.warning("Unpacked JVM missing 'release' file. Assuming valid version.");
+                    return true;
+                }
+
+                long vmvers = VersionUtil.readReleaseVersion(relfile, _javaVersionRegex);
+                if (vmvers == 0L) {
+                    log.warning("Unable to read version from 'release' file. Assuming valid.");
+                    return true;
+                }
+
+                version = vmvers;
+                log.info("Checking version of unpacked JVM [vers=" + version + "].");
+            }
+
+            if (_javaExactVersionRequired) {
+                if (version == _javaMinVersion) return true;
+                else {
+                    log.warning("An exact Java VM version is required.", "current", version,
+                                "required", _javaMinVersion);
+                    return false;
+                }
+            }
+
+            boolean minVersionOK = (_javaMinVersion == 0) || (version >= _javaMinVersion);
+            boolean maxVersionOK = (_javaMaxVersion == 0) || (version <= _javaMaxVersion);
+            return minVersionOK && maxVersionOK;
+
+        } catch (RuntimeException re) {
+            // if we can't parse the java version we're in weird land and should probably just try
+            // our luck with what we've got rather than try to download a new jvm
+            log.warning("Unable to parse VM version, hoping for the best",
+                        "error", re, "needed", _javaMinVersion);
+            return true;
+        }
+    }
+
+    /**
+     * Checks whether the app has a set of "optimum" JVM args that we wish to try first, detecting
+     * whether the launch is successful and, if necessary, trying again without the optimum
+     * arguments.
+     */
+    public boolean hasOptimumJvmArgs ()
+    {
+        return _optimumJvmArgs != null;
+    }
+
+    /**
+     * Returns true if the app should attempt to run even if we have no Internet connection.
+     */
+    public boolean allowOffline ()
+    {
+        return _allowOffline;
+    }
+
+    /**
+     * Attempts to redownload the <code>getdown.txt</code> file based on information parsed from a
+     * previous call to {@link #init}.
+     */
+    public void attemptRecovery (StatusDisplay status)
+        throws IOException
+    {
+        status.updateStatus("m.updating_metadata");
+        downloadConfigFile();
+    }
+
+    /**
+     * Downloads and replaces the <code>getdown.txt</code> and <code>digest.txt</code> files with
+     * those for the target version of our application.
+     */
+    public void updateMetadata ()
+        throws IOException
+    {
+        try {
+            // update our versioned application base with the target version
+            _vappbase = createVAppBase(_targetVersion);
+        } catch (MalformedURLException mue) {
+            String err = MessageUtil.tcompose("m.invalid_appbase", _appbase);
+            throw (IOException) new IOException(err).initCause(mue);
+        }
+
+        try {
+            // now re-download our control files; we download the digest first so that if it fails,
+            // our config file will still reference the old version and re-running the updater will
+            // start the whole process over again
+            downloadDigestFiles();
+            downloadConfigFile();
+
+        } catch (IOException ex) {
+            // if we are allowing offline execution, we want to allow the application to run in its
+            // current form rather than aborting the entire process; to do this, we delete the
+            // version.txt file and "trick" Getdown into thinking that it just needs to validate
+            // the application as is; next time the app runs when connected to the internet, it
+            // will have to rediscover that it needs updating and reattempt to update itself
+            if (_allowOffline) {
+                log.warning("Failed to update digest files.  Attempting offline operaton.", ex);
+                if (!FileUtil.deleteHarder(getLocalPath(VERSION_FILE))) {
+                    log.warning("Deleting version.txt failed.  This probably isn't going to work.");
+                }
+            } else {
+                throw ex;
+            }
+        }
+    }
+
+    /**
+     * Invokes the process associated with this application definition.
+     *
+     * @param optimum whether or not to include the set of optimum arguments (as opposed to falling
+     * back).
+     */
+    public Process createProcess (boolean optimum)
+        throws IOException
+    {
+        ArrayList<String> args = new ArrayList<>();
+
+        // reconstruct the path to the JVM
+        args.add(LaunchUtil.getJVMPath(getAppDir(), _windebug || optimum));
+
+        // check whether we're using -jar mode or -classpath mode
+        boolean dashJarMode = MANIFEST_CLASS.equals(_class);
+
+        // add the -classpath arguments if we're not in -jar mode
+        ClassPath classPath = PathBuilder.buildClassPath(this);
+        if (!dashJarMode) {
+            args.add("-classpath");
+            args.add(classPath.asArgumentString());
+        }
+
+        // we love our Mac users, so we do nice things to preserve our application identity
+        if (LaunchUtil.isMacOS()) {
+            args.add("-Xdock:icon=" + getLocalPath(_dockIconPath).getAbsolutePath());
+            args.add("-Xdock:name=" + _dockName);
+        }
+
+        // pass along our proxy settings
+        String proxyHost;
+        if ((proxyHost = System.getProperty("http.proxyHost")) != null) {
+            args.add("-Dhttp.proxyHost=" + proxyHost);
+            args.add("-Dhttp.proxyPort=" + System.getProperty("http.proxyPort"));
+            args.add("-Dhttps.proxyHost=" + proxyHost);
+            args.add("-Dhttps.proxyPort=" + System.getProperty("http.proxyPort"));
+        }
+
+        // add the marker indicating the app is running in getdown
+        args.add("-D" + Properties.GETDOWN + "=true");
+
+        // set the native library path if we have native resources
+        // @TODO optional getdown.txt parameter to set addCurrentLibraryPath to true or false?
+        ClassPath javaLibPath = PathBuilder.buildLibsPath(this, true);
+        if (javaLibPath != null) {
+            args.add("-Djava.library.path=" + javaLibPath.asArgumentString());
+        }
+
+        // pass along any pass-through arguments
+        for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
+            String key = (String)entry.getKey();
+            if (key.startsWith(PROP_PASSTHROUGH_PREFIX)) {
+                key = key.substring(PROP_PASSTHROUGH_PREFIX.length());
+                args.add("-D" + key + "=" + entry.getValue());
+            }
+        }
+
+        // add the JVM arguments
+        for (String string : _jvmargs) {
+            args.add(processArg(string));
+        }
+
+        // add the optimum arguments if requested and available
+        if (optimum && _optimumJvmArgs != null) {
+            for (String string : _optimumJvmArgs) {
+                args.add(processArg(string));
+            }
+        }
+
+        // add the arguments from extra.txt (after the optimum ones, in case they override them)
+        for (String string : _txtJvmArgs) {
+            args.add(processArg(string));
+        }
+
+        // if we're in -jar mode add those arguments, otherwise add the app class name
+        if (dashJarMode) {
+            args.add("-jar");
+            args.add(classPath.asArgumentString());
+        } else {
+            args.add(_class);
+        }
+
+        // almost finally check the startup file arguments
+        for (File f : _startupFiles) {
+          _appargs.add(f.getAbsolutePath());
+          break; // Only add one file to open
+        }
+        
+        // check if one arg with recognised extension
+        if ( _appargs.size() == 1 && _appargs.get(0) != null ) {
+          String filename = _appargs.get(0);
+          String ext = null;
+          int j = filename.lastIndexOf('.');
+          if (j > -1) {
+            ext = filename.substring(j+1);
+          }
+          if (ext != null && LOCATOR_FILE_EXTENSION.equals(ext.toLowerCase())) {
+            // this file extension should have been dealt with in Getdown class
+          } else {
+            _appargs.add(0, "-open");
+          }
+        }
+
+        // finally add the application arguments
+        for (String string : _appargs) {
+            args.add(processArg(string));
+        }
+        
+        String[] envp = createEnvironment();
+        String[] sargs = args.toArray(new String[args.size()]);
+        log.info("Running " + StringUtil.join(sargs, "\n  "));
+
+        return Runtime.getRuntime().exec(sargs, envp, getAppDir());
+    }
+
+    /**
+     * If the application provided environment variables, combine those with the current
+     * environment and return that in a style usable for {@link Runtime#exec(String, String[])}.
+     * If the application didn't provide any environment variables, null is returned to just use
+     * the existing environment.
+     */
+    protected String[] createEnvironment ()
+    {
+        List<String> envvar = new ArrayList<>();
+        fillAssignmentListFromPairs("env.txt", envvar);
+        if (envvar.isEmpty()) {
+            log.info("Didn't find any custom environment variables, not setting any.");
+            return null;
+        }
+
+        List<String> envAssignments = new ArrayList<>();
+        for (String assignment : envvar) {
+            envAssignments.add(processArg(assignment));
+        }
+        for (Map.Entry<String, String> environmentEntry : System.getenv().entrySet()) {
+            envAssignments.add(environmentEntry.getKey() + "=" + environmentEntry.getValue());
+        }
+        String[] envp = envAssignments.toArray(new String[envAssignments.size()]);
+        log.info("Environment " + StringUtil.join(envp, "\n "));
+        return envp;
+    }
+
+    /**
+     * Runs this application directly in the current VM.
+     */
+    public void invokeDirect () throws IOException
+    {
+        ClassPath classPath = PathBuilder.buildClassPath(this);
+        URL[] jarUrls = classPath.asUrls();
+
+        // create custom class loader
+        URLClassLoader loader = new URLClassLoader(jarUrls, ClassLoader.getSystemClassLoader()) {
+            @Override protected PermissionCollection getPermissions (CodeSource code) {
+                Permissions perms = new Permissions();
+                perms.add(new AllPermission());
+                return perms;
+            }
+        };
+        Thread.currentThread().setContextClassLoader(loader);
+
+        log.info("Configured URL class loader:");
+        for (URL url : jarUrls) log.info("  " + url);
+
+        // configure any system properties that we can
+        for (String jvmarg : _jvmargs) {
+            if (jvmarg.startsWith("-D")) {
+                jvmarg = processArg(jvmarg.substring(2));
+                int eqidx = jvmarg.indexOf("=");
+                if (eqidx == -1) {
+                    log.warning("Bogus system property: '" + jvmarg + "'?");
+                } else {
+                    System.setProperty(jvmarg.substring(0, eqidx), jvmarg.substring(eqidx+1));
+                }
+            }
+        }
+
+        // pass along any pass-through arguments
+        Map<String, String> passProps = new HashMap<>();
+        for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) {
+            String key = (String)entry.getKey();
+            if (key.startsWith(PROP_PASSTHROUGH_PREFIX)) {
+                key = key.substring(PROP_PASSTHROUGH_PREFIX.length());
+                passProps.put(key, (String)entry.getValue());
+            }
+        }
+        // we can't set these in the above loop lest we get a ConcurrentModificationException
+        for (Map.Entry<String, String> entry : passProps.entrySet()) {
+            System.setProperty(entry.getKey(), entry.getValue());
+        }
+
+        // prepare our app arguments
+        String[] args = new String[_appargs.size()];
+        for (int ii = 0; ii < args.length; ii++) args[ii] = processArg(_appargs.get(ii));
+
+        try {
+            log.info("Loading " + _class);
+            Class<?> appclass = loader.loadClass(_class);
+            Method main = appclass.getMethod("main", EMPTY_STRING_ARRAY.getClass());
+            log.info("Invoking main({" + StringUtil.join(args, ", ") + "})");
+            main.invoke(null, new Object[] { args });
+        } catch (Exception e) {
+            log.warning("Failure invoking app main", e);
+        }
+    }
+
+    /** Replaces the application directory and version in any argument. */
+    protected String processArg (String arg)
+    {
+        arg = arg.replace("%APPDIR%", getAppDir().getAbsolutePath());
+        arg = arg.replace("%VERSION%", String.valueOf(_version));
+
+        // if this argument contains %ENV.FOO% replace those with the associated values looked up
+        // from the environment
+        if (arg.contains(ENV_VAR_PREFIX)) {
+            StringBuffer sb = new StringBuffer();
+            Matcher matcher = ENV_VAR_PATTERN.matcher(arg);
+            while (matcher.find()) {
+                String varName = matcher.group(1), varValue = System.getenv(varName);
+                String repValue = varValue == null ? "MISSING:"+varName : varValue;
+                matcher.appendReplacement(sb, Matcher.quoteReplacement(repValue));
+            }
+            matcher.appendTail(sb);
+            arg = sb.toString();
+        }
+
+        return arg;
+    }
+
+    /**
+     * Loads the <code>digest.txt</code> file and verifies the contents of both that file and the
+     * <code>getdown.text</code> file. Then it loads the <code>version.txt</code> and decides
+     * whether or not the application needs to be updated or whether we can proceed to verification
+     * and execution.
+     *
+     * @return true if the application needs to be updated, false if it is up to date and can be
+     * verified and executed.
+     *
+     * @exception IOException thrown if we encounter an unrecoverable error while verifying the
+     * metadata.
+     */
+    public boolean verifyMetadata (StatusDisplay status)
+        throws IOException
+    {
+        log.info("Verifying application: " + _vappbase);
+        log.info("Version: " + _version);
+        log.info("Class: " + _class);
+
+        // this will read in the contents of the digest file and validate itself
+        try {
+            _digest = new Digest(getAppDir(), _strictComments);
+        } catch (IOException ioe) {
+            log.info("Failed to load digest: " + ioe.getMessage() + ". Attempting recovery...");
+        }
+
+        // if we have no version, then we are running in unversioned mode so we need to download
+        // our digest.txt file on every invocation
+        if (_version == -1) {
+            // make a note of the old meta-digest, if this changes we need to revalidate all of our
+            // resources as one or more of them have also changed
+            String olddig = (_digest == null) ? "" : _digest.getMetaDigest();
+            try {
+                status.updateStatus("m.checking");
+                downloadDigestFiles();
+                _digest = new Digest(getAppDir(), _strictComments);
+                if (!olddig.equals(_digest.getMetaDigest())) {
+                    log.info("Unversioned digest changed. Revalidating...");
+                    status.updateStatus("m.validating");
+                    clearValidationMarkers();
+                }
+            } catch (IOException ioe) {
+                log.warning("Failed to refresh non-versioned digest: " +
+                            ioe.getMessage() + ". Proceeding...");
+            }
+        }
+
+        // regardless of whether we're versioned, if we failed to read the digest from disk, try to
+        // redownload the digest file and give it another good college try; this time we allow
+        // exceptions to propagate up to the caller as there is nothing else we can do
+        if (_digest == null) {
+            status.updateStatus("m.updating_metadata");
+            downloadDigestFiles();
+            _digest = new Digest(getAppDir(), _strictComments);
+        }
+
+        // now verify the contents of our main config file
+        Resource crsrc = getConfigResource();
+        if (!_digest.validateResource(crsrc, null)) {
+            status.updateStatus("m.updating_metadata");
+            // attempt to redownload both of our metadata files; again we pass errors up to our
+            // caller because there's nothing we can do to automatically recover
+            downloadConfigFile();
+            downloadDigestFiles();
+            _digest = new Digest(getAppDir(), _strictComments);
+            // revalidate everything if we end up downloading new metadata
+            clearValidationMarkers();
+            // if the new copy validates, reinitialize ourselves; otherwise report baffling hoseage
+            if (_digest.validateResource(crsrc, null)) {
+                init(true);
+            } else {
+                log.warning(CONFIG_FILE + " failed to validate even after redownloading. " +
+                            "Blindly forging onward.");
+            }
+        }
+
+        // start by assuming we are happy with our version
+        _targetVersion = _version;
+
+        // if we are a versioned application, read in the contents of the version.txt file
+        // and/or check the latest config URL for a newer version
+        if (_version != -1) {
+            File vfile = getLocalPath(VERSION_FILE);
+            long fileVersion = VersionUtil.readVersion(vfile);
+            if (fileVersion != -1) {
+                _targetVersion = fileVersion;
+            }
+
+            if (_latest != null) {
+                try (InputStream in = ConnectionUtil.open(proxy, _latest, 0, 0).getInputStream();
+                     InputStreamReader reader = new InputStreamReader(in, UTF_8);
+                     BufferedReader bin = new BufferedReader(reader)) {
+                    for (String[] pair : Config.parsePairs(bin, Config.createOpts(false))) {
+                        if (pair[0].equals("version")) {
+                            _targetVersion = Math.max(Long.parseLong(pair[1]), _targetVersion);
+                            if (fileVersion != -1 && _targetVersion > fileVersion) {
+                                // replace the file with the newest version
+                                try (FileOutputStream fos = new FileOutputStream(vfile);
+                                     PrintStream out = new PrintStream(fos)) {
+                                    out.println(_targetVersion);
+                                }
+                            }
+                            break;
+                        }
+                    }
+                } catch (Exception e) {
+                    log.warning("Unable to retrieve version from latest config file.", e);
+                }
+            }
+        }
+
+        // finally let the caller know if we need an update
+        return _version != _targetVersion;
+    }
+
+    /**
+     * Verifies the code and media resources associated with this application. A list of resources
+     * that do not exist or fail the verification process will be returned. If all resources are
+     * ready to go, null will be returned and the application is considered ready to run.
+     *
+     * @param obs a progress observer that will be notified of verification progress. NOTE: this
+     * observer may be called from arbitrary threads, so if you update a UI based on calls to it,
+     * you have to take care to get back to your UI thread.
+     * @param alreadyValid if non-null a 1 element array that will have the number of "already
+     * validated" resources filled in.
+     * @param unpacked a set to populate with unpacked resources.
+     * @param toInstall a list into which to add resources that need to be installed.
+     * @param toDownload a list into which to add resources that need to be downloaded.
+     */
+    public void verifyResources (
+        ProgressObserver obs, int[] alreadyValid, Set<Resource> unpacked,
+        Set<Resource> toInstall, Set<Resource> toDownload)
+        throws InterruptedException
+    {
+        // resources are verified on background threads supplied by the thread pool, and progress
+        // is reported by posting runnable actions to the actions queue which is processed by the
+        // main (UI) thread
+        ExecutorService exec = Executors.newFixedThreadPool(SysProps.threadPoolSize());
+        final BlockingQueue<Runnable> actions = new LinkedBlockingQueue<Runnable>();
+        final int[] completed = new int[1];
+
+        long start = System.currentTimeMillis();
+
+        // obtain the sizes of the resources to validate
+        List<Resource> rsrcs = getAllActiveResources();
+        long[] sizes = new long[rsrcs.size()];
+        long totalSize = 0;
+        for (int ii = 0; ii < sizes.length; ii++) {
+            totalSize += sizes[ii] = rsrcs.get(ii).getLocal().length();
+        }
+        final ProgressObserver fobs = obs;
+        // as long as we forward aggregated progress updates to the UI thread, having multiple
+        // threads update a progress aggregator is "mostly" thread-safe
+        final ProgressAggregator pagg = new ProgressAggregator(new ProgressObserver() {
+            public void progress (final int percent) {
+                actions.add(new Runnable() {
+                    public void run () {
+                        fobs.progress(percent);
+                    }
+                });
+            }
+        }, sizes);
+
+        final int[] fAlreadyValid = alreadyValid;
+        final Set<Resource> toInstallAsync = new ConcurrentSkipListSet<>(toInstall);
+        final Set<Resource> toDownloadAsync = new ConcurrentSkipListSet<>();
+        final Set<Resource> unpackedAsync = new ConcurrentSkipListSet<>();
+
+        for (int ii = 0; ii < sizes.length; ii++) {
+            final Resource rsrc = rsrcs.get(ii);
+            final int index = ii;
+            exec.execute(new Runnable() {
+                public void run () {
+                    verifyResource(rsrc, pagg.startElement(index), fAlreadyValid,
+                                   unpackedAsync, toInstallAsync, toDownloadAsync);
+                    actions.add(new Runnable() {
+                        public void run () {
+                            completed[0] += 1;
+                        }
+                    });
+                }
+            });
+        }
+
+        while (completed[0] < rsrcs.size()) {
+            // we should be getting progress completion updates WAY more often than one every
+            // minute, so if things freeze up for 60 seconds, abandon ship
+            Runnable action = actions.poll(60, TimeUnit.SECONDS);
+            action.run();
+        }
+
+        exec.shutdown();
+
+        toInstall.addAll(toInstallAsync);
+        toDownload.addAll(toDownloadAsync);
+        unpacked.addAll(unpackedAsync);
+
+        long complete = System.currentTimeMillis();
+        log.info("Verified resources", "count", rsrcs.size(), "size", (totalSize/1024) + "k",
+                 "duration", (complete-start) + "ms");
+    }
+
+    private void verifyResource (Resource rsrc, ProgressObserver obs, int[] alreadyValid,
+                                 Set<Resource> unpacked,
+                                 Set<Resource> toInstall, Set<Resource> toDownload) {
+        if (rsrc.isMarkedValid()) {
+            if (alreadyValid != null) {
+                alreadyValid[0]++;
+            }
+            obs.progress(100);
+            return;
+        }
+
+        try {
+            if (_digest.validateResource(rsrc, obs)) {
+                // if the resource has a _new file, add it to to-install list
+                if (rsrc.getLocalNew().exists()) {
+                    toInstall.add(rsrc);
+                    return;
+                }
+                rsrc.applyAttrs();
+                unpacked.add(rsrc);
+                rsrc.markAsValid();
+                return;
+            }
+
+        } catch (Exception e) {
+            log.info("Failure verifying resource. Requesting redownload...",
+                     "rsrc", rsrc, "error", e);
+
+        } finally {
+            obs.progress(100);
+        }
+        toDownload.add(rsrc);
+    }
+
+    /**
+     * Unpacks the resources that require it (we know that they're valid).
+     *
+     * @param unpacked a set of resources to skip because they're already unpacked.
+     */
+    public void unpackResources (ProgressObserver obs, Set<Resource> unpacked)
+        throws InterruptedException
+    {
+        List<Resource> rsrcs = getActiveResources();
+
+        // remove resources that we don't want to unpack
+        for (Iterator<Resource> it = rsrcs.iterator(); it.hasNext(); ) {
+            Resource rsrc = it.next();
+            if (!rsrc.shouldUnpack() || unpacked.contains(rsrc)) {
+                it.remove();
+            }
+        }
+
+        // obtain the sizes of the resources to unpack
+        long[] sizes = new long[rsrcs.size()];
+        for (int ii = 0; ii < sizes.length; ii++) {
+            sizes[ii] = rsrcs.get(ii).getLocal().length();
+        }
+
+        ProgressAggregator pagg = new ProgressAggregator(obs, sizes);
+        for (int ii = 0; ii < sizes.length; ii++) {
+            Resource rsrc = rsrcs.get(ii);
+            ProgressObserver pobs = pagg.startElement(ii);
+            try {
+                rsrc.unpack();
+            } catch (IOException ioe) {
+                log.warning("Failure unpacking resource", "rsrc", rsrc, ioe);
+            }
+            pobs.progress(100);
+        }
+    }
+
+    /**
+     * Clears all validation marker files.
+     */
+    public void clearValidationMarkers ()
+    {
+        clearValidationMarkers(getAllActiveResources().iterator());
+    }
+
+    /**
+     * Returns the version number for the application.  Should only be called after successful
+     * return of verifyMetadata.
+     */
+    public long getVersion ()
+    {
+        return _version;
+    }
+
+    /**
+     * Creates a versioned application base URL for the specified version.
+     */
+    protected URL createVAppBase (long version)
+        throws MalformedURLException
+    {
+        String url = version < 0 ? _appbase : _appbase.replace("%VERSION%", "" + version);
+        return HostWhitelist.verify(new URL(url));
+    }
+
+    /**
+     * Clears all validation marker files for the resources in the supplied iterator.
+     */
+    protected void clearValidationMarkers (Iterator<Resource> iter)
+    {
+        while (iter.hasNext()) {
+            iter.next().clearMarker();
+        }
+    }
+
+    /**
+     * Downloads a new copy of CONFIG_FILE.
+     */
+    protected void downloadConfigFile ()
+        throws IOException
+    {
+        downloadControlFile(CONFIG_FILE, 0);
+    }
+
+    /**
+     * @return true if gettingdown.lock was unlocked, already locked by this application or if
+     * we're not locking at all.
+     */
+    public synchronized boolean lockForUpdates ()
+    {
+        if (_lock != null && _lock.isValid()) {
+            return true;
+        }
+        try {
+            _lockChannel = new RandomAccessFile(getLocalPath("gettingdown.lock"), "rw").getChannel();
+        } catch (FileNotFoundException e) {
+            log.warning("Unable to create lock file", "message", e.getMessage(), e);
+            return false;
+        }
+        try {
+            _lock = _lockChannel.tryLock();
+        } catch (IOException e) {
+            log.warning("Unable to create lock", "message", e.getMessage(), e);
+            return false;
+        } catch (OverlappingFileLockException e) {
+            log.warning("The lock is held elsewhere in this JVM", e);
+            return false;
+        }
+        log.info("Able to lock for updates: " + (_lock != null));
+        return _lock != null;
+    }
+
+    /**
+     * Release gettingdown.lock
+     */
+    public synchronized void releaseLock ()
+    {
+        if (_lock != null) {
+            log.info("Releasing lock");
+            try {
+                _lock.release();
+            } catch (IOException e) {
+                log.warning("Unable to release lock", "message", e.getMessage(), e);
+            }
+            try {
+                _lockChannel.close();
+            } catch (IOException e) {
+                log.warning("Unable to close lock channel", "message", e.getMessage(), e);
+            }
+            _lockChannel = null;
+            _lock = null;
+        }
+    }
+
+    /**
+     * Downloads the digest files and validates their signature.
+     * @throws IOException
+     */
+    protected void downloadDigestFiles ()
+        throws IOException
+    {
+        for (int version = 1; version <= Digest.VERSION; version++) {
+            downloadControlFile(Digest.digestFile(version), version);
+        }
+    }
+
+    /**
+     * Downloads a new copy of the specified control file, optionally validating its signature.
+     * If the download is successful, moves it over the old file on the filesystem.
+     *
+     * <p> TODO: Switch to PKCS #7 or CMS.
+     *
+     * @param sigVersion if {@code 0} no validation will be performed, if {@code > 0} then this
+     * should indicate the version of the digest file being validated which indicates which
+     * algorithm to use to verify the signature. See {@link Digest#VERSION}.
+     */
+    protected void downloadControlFile (String path, int sigVersion)
+        throws IOException
+    {
+        File target = downloadFile(path);
+
+        if (sigVersion > 0) {
+            if (_envc.certs.isEmpty()) {
+                log.info("No signing certs, not verifying digest.txt", "path", path);
+
+            } else {
+                File signatureFile = downloadFile(path + SIGNATURE_SUFFIX);
+                byte[] signature = null;
+                try (FileInputStream signatureStream = new FileInputStream(signatureFile)) {
+                    signature = StreamUtil.toByteArray(signatureStream);
+                } finally {
+                    FileUtil.deleteHarder(signatureFile); // delete the file regardless
+                }
+
+                byte[] buffer = new byte[8192];
+                int length, validated = 0;
+                for (Certificate cert : _envc.certs) {
+                    try (FileInputStream dataInput = new FileInputStream(target)) {
+                        Signature sig = Signature.getInstance(Digest.sigAlgorithm(sigVersion));
+                        sig.initVerify(cert);
+                        while ((length = dataInput.read(buffer)) != -1) {
+                            sig.update(buffer, 0, length);
+                        }
+
+                        if (!sig.verify(Base64.decode(signature, Base64.DEFAULT))) {
+                            log.info("Signature does not match", "cert", cert.getPublicKey());
+                            continue;
+                        } else {
+                            log.info("Signature matches", "cert", cert.getPublicKey());
+                            validated++;
+                        }
+
+                    } catch (IOException ioe) {
+                        log.warning("Failure validating signature of " + target + ": " + ioe);
+
+                    } catch (GeneralSecurityException gse) {
+                        // no problem!
+
+                    }
+                }
+
+                // if we couldn't find a key that validates our digest, we are the hosed!
+                if (validated == 0) {
+                    // delete the temporary digest file as we know it is invalid
+                    FileUtil.deleteHarder(target);
+                    throw new IOException("m.corrupt_digest_signature_error");
+                }
+            }
+        }
+
+        // now move the temporary file over the original
+        File original = getLocalPath(path);
+        if (!FileUtil.renameTo(target, original)) {
+            throw new IOException("Failed to rename(" + target + ", " + original + ")");
+        }
+    }
+
+    /**
+     * Download a path to a temporary file, returning a {@link File} instance with the path
+     * contents.
+     */
+    protected File downloadFile (String path)
+        throws IOException
+    {
+        File target = getLocalPath(path + "_new");
+
+        URL targetURL = null;
+        try {
+            targetURL = getRemoteURL(path);
+        } catch (Exception e) {
+            log.warning("Requested to download invalid control file",
+                "appbase", _vappbase, "path", path, "error", e);
+            throw (IOException) new IOException("Invalid path '" + path + "'.").initCause(e);
+        }
+
+        log.info("Attempting to refetch '" + path + "' from '" + targetURL + "'.");
+
+        // stream the URL into our temporary file
+        URLConnection uconn = ConnectionUtil.open(proxy, targetURL, 0, 0);
+        // we have to tell Java not to use caches here, otherwise it will cache any request for
+        // same URL for the lifetime of this JVM (based on the URL string, not the URL object);
+        // if the getdown.txt file, for example, changes in the meanwhile, we would never hear
+        // about it; turning off caches is not a performance concern, because when Getdown asks
+        // to download a file, it expects it to come over the wire, not from a cache
+        uconn.setUseCaches(false);
+        uconn.setRequestProperty("Accept-Encoding", "gzip");
+        try (InputStream fin = uconn.getInputStream()) {
+            String encoding = uconn.getContentEncoding();
+            boolean gzip = "gzip".equalsIgnoreCase(encoding);
+            try (InputStream fin2 = (gzip ? new GZIPInputStream(fin) : fin)) {
+                try (FileOutputStream fout = new FileOutputStream(target)) {
+                    StreamUtil.copy(fin2, fout);
+                }
+            }
+        }
+
+        return target;
+    }
+
+    /** Helper function for creating {@link Resource} instances. */
+    protected Resource createResource (String path, EnumSet<Resource.Attr> attrs)
+        throws MalformedURLException
+    {
+        return new Resource(path, getRemoteURL(path), getLocalPath(path), attrs);
+    }
+
+    /** Helper function to add all values in {@code values} (if non-null) to {@code target}. */
+    protected static void addAll (String[] values, List<String> target) {
+        if (values != null) {
+            for (String value : values) {
+                target.add(value);
+            }
+        }
+    }
+
+    /**
+     * Make an immutable List from the specified int array.
+     */
+    public static List<Integer> intsToList (int[] values)
+    {
+        List<Integer> list = new ArrayList<>(values.length);
+        for (int val : values) {
+            list.add(val);
+        }
+        return Collections.unmodifiableList(list);
+    }
+
+    /**
+     * Make an immutable List from the specified String array.
+     */
+    public static List<String> stringsToList (String[] values)
+    {
+        return values == null ? null : Collections.unmodifiableList(Arrays.asList(values));
+    }
+
+    /** Used to parse resources with the specified name. */
+    protected void parseResources (Config config, String name, EnumSet<Resource.Attr> attrs,
+                                   List<Resource> list)
+    {
+        String[] rsrcs = config.getMultiValue(name);
+        if (rsrcs == null) {
+            return;
+        }
+        for (String rsrc : rsrcs) {
+            try {
+                list.add(createResource(rsrc, attrs));
+            } catch (Exception e) {
+                log.warning("Invalid resource '" + rsrc + "'. " + e);
+            }
+        }
+    }
+
+    /** Possibly generates and returns a google analytics tracking cookie. */
+    protected String getGATrackingCode ()
+    {
+        if (_trackingGAHash == null) {
+            return "";
+        }
+        long time = System.currentTimeMillis() / 1000;
+        if (_trackingStart == 0) {
+            _trackingStart = time;
+        }
+        if (_trackingId == 0) {
+            int low = 100000000, high = 1000000000;
+            _trackingId = low + _rando.nextInt(high-low);
+        }
+        StringBuilder cookie = new StringBuilder("&utmcc=__utma%3D").append(_trackingGAHash);
+        cookie.append(".").append(_trackingId);
+        cookie.append(".").append(_trackingStart).append(".").append(_trackingStart);
+        cookie.append(".").append(time).append(".1%3B%2B");
+        cookie.append("__utmz%3D").append(_trackingGAHash).append(".");
+        cookie.append(_trackingStart).append(".1.1.");
+        cookie.append("utmcsr%3D(direct)%7Cutmccn%3D(direct)%7Cutmcmd%3D(none)%3B");
+        int low = 1000000000, high = 2000000000;
+        cookie.append("&utmn=").append(_rando.nextInt(high-low));
+        return cookie.toString();
+    }
+
+    /**
+     * Encodes a path for use in a URL.
+     */
+    protected static String encodePath (String path)
+    {
+        try {
+            // we want to keep slashes because we're encoding an entire path; also we need to turn
+            // + into %20 because web servers don't like + in paths or file names, blah
+            return URLEncoder.encode(path, "UTF-8").replace("%2F", "/").replace("+", "%20");
+        } catch (UnsupportedEncodingException ue) {
+            log.warning("Failed to URL encode " + path + ": " + ue);
+            return path;
+        }
+    }
+
+    protected File getLocalPath (File appdir, String path)
+    {
+        return new File(appdir, path);
+    }
+
+    public static void setStartupFilesFromParameterString(String p) {
+      // multiple files *might* be passed in as space separated quoted filenames
+      String q = "\"";
+      if (!StringUtil.isBlank(p)) {
+        String[] filenames;
+        // split quoted params or treat as single string array
+        if (p.startsWith(q) && p.endsWith(q)) {
+          // this fails if, e.g.
+          // p=q("stupidfilename\" " "otherfilename")
+          // let's hope no-one ever ends a filename with '" '
+          filenames = p.substring(q.length(),p.length()-q.length()).split(q+" "+q);
+        } else {
+          // single unquoted filename
+          filenames = new String[]{p};
+        }
+
+        // check for locator file.  Only allow one locator file to be double clicked (if multiple files opened, ignore locator files)
+        String locatorFilename = filenames.length >= 1 ? filenames[0] : null;
+        if (
+                !StringUtil.isBlank(locatorFilename)
+                && locatorFilename.toLowerCase().endsWith("."+Application.LOCATOR_FILE_EXTENSION)
+                ) {
+          setLocatorFile(locatorFilename);
+          // remove the locator filename from the filenames array
+          String[] otherFilenames = new String[filenames.length - 1];
+          System.arraycopy(filenames, 1, otherFilenames, 0, otherFilenames.length);
+          filenames = otherFilenames;
+        }
+
+        for (int i = 0; i < filenames.length; i++) {
+          String filename = filenames[i];
+          // skip any other locator files in a multiple file list
+          if (! filename.toLowerCase().endsWith("."+Application.LOCATOR_FILE_EXTENSION)) {
+            addStartupFile(filename);
+          }
+        }
+      }
+    }
+    
+    public static void setLocatorFile(String filename) {
+      _locatorFile = new File(filename);
+    }
+    
+    public static void addStartupFile(String filename) {
+      _startupFiles.add(new File(filename));
+    }
+    
+    private Config createLocatorConfig(Config.ParseOpts opts) {
+      if (_locatorFile == null) {
+        return null;
+      }
+      
+      Config locatorConfig = null;
+      
+      try {
+        Config tmpConfig = null;
+        Map<String, Object> tmpData = new HashMap<>();
+        if (_locatorFile.exists()) {
+          tmpConfig = Config.parseConfig(_locatorFile,  opts);
+          // appbase is sanitised in HostWhitelist
+          Map<String, Object> tmpConfigData = tmpConfig.getData();
+          if (tmpConfig != null) {
+            for (Map.Entry<String, Object> entry : tmpConfigData.entrySet()) {
+              String key = entry.getKey();
+              Object value = entry.getValue();
+              String mkey = key.indexOf('.') > -1 ? key.substring(key.indexOf('.') + 1) : key;
+              if (Config.allowedReplaceKeys.contains(mkey) || Config.allowedMergeKeys.contains(mkey)) {
+                tmpData.put(key, value);
+              }
+            }
+          } else {
+            log.warning("Error occurred reading config file", "file", _locatorFile);
+          }
+        } else {
+          log.warning("Given locator file does not exist", "file", _locatorFile);
+        }
+        
+        locatorConfig = new Config(tmpData);
+        
+      } catch (Exception e) {
+        log.warning("Failure reading locator file",  "file", _locatorFile, e);
+      }
+      
+      return locatorConfig;
+    }
+    
+    public String getAppbase() {
+       return _appbase;
+    }
+    
+    protected final EnvConfig _envc;
+    protected File _config;
+    protected File _backupConfig;
+    protected Digest _digest;
+
+    protected long _version = -1;
+    protected long _targetVersion = -1;
+    protected String _appbase;
+    protected URL _vappbase;
+    protected URL _latest;
+    protected String _class;
+    protected String _dockName;
+    protected String _dockIconPath;
+    protected boolean _strictComments;
+    protected boolean _windebug;
+    protected boolean _allowOffline;
+    protected int _maxConcDownloads;
+
+    protected String _trackingURL;
+    protected Set<Integer> _trackingPcts;
+    protected String _trackingCookieName;
+    protected String _trackingCookieProperty;
+    protected String _trackingURLSuffix;
+    protected String _trackingGAHash;
+    protected long _trackingStart;
+    protected int _trackingId;
+
+    protected String _javaVersionProp = "java.version";
+    protected String _javaVersionRegex = "(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(_\\d+)?)?)?";
+    protected long _javaMinVersion, _javaMaxVersion;
+    protected boolean _javaExactVersionRequired;
+    protected String _javaLocation;
+
+    protected List<Resource> _codes = new ArrayList<>();
+    protected List<Resource> _resources = new ArrayList<>();
+
+    protected boolean _useCodeCache;
+    protected int _codeCacheRetentionDays;
+
+    protected Map<String,AuxGroup> _auxgroups = new HashMap<>();
+    protected Map<String,Boolean> _auxactive = new HashMap<>();
+
+    protected List<String> _jvmargs = new ArrayList<>();
+    protected List<String> _appargs = new ArrayList<>();
+
+    protected String[] _optimumJvmArgs;
+
+    protected List<String> _txtJvmArgs = new ArrayList<>();
+
+    /** If a warning has been issued about not being able to set modtimes. */
+    protected boolean _warnedAboutSetLastModified;
+
+    /** Locks gettingdown.lock in the app dir. Held the entire time updating is going on.*/
+    protected FileLock _lock;
+
+    /** Channel to the file underlying _lock.  Kept around solely so the lock doesn't close. */
+    protected FileChannel _lockChannel;
+
+    protected Random _rando = new Random();
+
+    protected static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+    protected static final String ENV_VAR_PREFIX = "%ENV.";
+    protected static final Pattern ENV_VAR_PATTERN = Pattern.compile("%ENV\\.(.*?)%");
+    protected static File _locatorFile;
+    protected static List<File> _startupFiles = new ArrayList<>();
+    public static final String LOCATOR_FILE_EXTENSION = "jvl";
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Build.java.tmpl b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Build.java.tmpl
new file mode 100644 (file)
index 0000000..1023ede
--- /dev/null
@@ -0,0 +1,76 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2016 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.threerings.getdown.util.StringUtil;
+
+/**
+ * Contains static data provided during the build process.
+ */
+public class Build {
+
+    /** The date and time at which the code was built: in {@code yyyy-MM-dd HH:mm} format. */
+    public static String time () {
+        return "@build_time@";
+    }
+
+    /** The Maven version of the Getdown project. */
+    public static String version () {
+        return "@build_version@";
+    }
+
+    /**
+     * <p>The hosts which Getdown is allowed to communicate with. An empty list indicates that
+     * no whitelist is configured and there are no limitations. By default, no host whitelist
+     * is added to the binary, so it can be used to download and run applications from any
+     * server.
+     *
+     * <p>To create a custom Getdown build that can only talk to whitelisted servers, set
+     * the {@code getdown.host.whitelist} property on the command line while building the JAR
+     * (e.g. {@code mvn package -Dgetdown.host.whitelist=my.server.com}). Wildcards can be used
+     * (e.g. {@code *.mycompany.com}) and multiple values can be separated by commas
+     * (e.g. {@code app1.foo.com,app2.bar.com,app3.baz.com}).
+     */
+    public static List<String> hostWhitelist () {
+        return Arrays.asList(StringUtil.parseStringArray("@host_whitelist@"));
+    }
+    
+    /*
+     * <p>The default connect_timeout to use.  Overridden by system property of the same name at runtime
+     */
+    public static int defaultConnectTimeout () {
+       try {
+                       return Integer.valueOf("@connect_timeout@");
+               } catch (Exception e) {
+                       return 0;
+               }
+    }
+    
+    /*
+     * <p>The default read_timeout to use.  Overridden by system property of the same name at runtime
+     */
+    public static int defaultReadTimeout () {
+       try {
+                       return Integer.valueOf("@read_timeout@");
+               } catch (Exception e) {
+                       return 30;
+               }
+    }
+    
+    /*
+     * <p>Whether to allow the local "file://" scheme for appbase
+     */
+    public static boolean allowLocatorFileProtocol() {
+       try {
+                       return Boolean.valueOf("@allow_file_protocol@");
+               } catch (Exception e) {
+                       return false;
+               }
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/ClassPath.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/ClassPath.java
new file mode 100644 (file)
index 0000000..9c2fce3
--- /dev/null
@@ -0,0 +1,76 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Represents the class path and it's elements of the application to be launched. The class path
+ * can either be represented as an {@link #asArgumentString() argument string} for the java command
+ * line or as an {@link #asUrls() array of URLs} to be used by a {@link URLClassLoader}.
+ */
+public class ClassPath
+{
+    public ClassPath (LinkedHashSet<File> classPathEntries)
+    {
+        _classPathEntries = Collections.unmodifiableSet(classPathEntries);
+    }
+
+    /**
+     * Returns the class path as an java command line argument string, e.g.
+     *
+     * <pre>
+     *   /path/to/a.jar:/path/to/b.jar
+     * </pre>
+     */
+    public String asArgumentString ()
+    {
+        StringBuilder builder = new StringBuilder();
+        String delimiter = "";
+        for (File entry: _classPathEntries) {
+            builder.append(delimiter).append(entry.getAbsolutePath());
+            delimiter = File.pathSeparator;
+        }
+        return builder.toString();
+    }
+
+    /**
+     * Returns the class path entries as an array of URLs to be used for example by an
+     * {@link URLClassLoader}.
+     */
+    public URL[] asUrls ()
+    {
+        URL[] urls = new URL[_classPathEntries.size()];
+        int i = 0;
+        for (File entry : _classPathEntries) {
+            urls[i++] = getURL(entry);
+        }
+        return urls;
+    }
+
+    public Set<File> getClassPathEntries ()
+    {
+        return _classPathEntries;
+    }
+
+
+    private static URL getURL (File file)
+    {
+        try {
+            return file.toURI().toURL();
+        } catch (MalformedURLException e) {
+            throw new IllegalStateException("URL of file is illegal: " + file.getAbsolutePath(), e);
+        }
+    }
+
+    private final Set<File> _classPathEntries;
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Digest.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Digest.java
new file mode 100644 (file)
index 0000000..bc8d140
--- /dev/null
@@ -0,0 +1,228 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import java.util.concurrent.*;
+
+import com.threerings.getdown.util.Config;
+import com.threerings.getdown.util.MessageUtil;
+import com.threerings.getdown.util.ProgressObserver;
+import com.threerings.getdown.util.StringUtil;
+
+import static com.threerings.getdown.Log.log;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Manages the <code>digest.txt</code> file and the computing and processing of digests for an
+ * application.
+ */
+public class Digest
+{
+    /** The current version of the digest protocol. */
+    public static final int VERSION = 2;
+
+    /**
+     * Returns the name of the digest file for the specified protocol version.
+     */
+    public static String digestFile (int version) {
+        String infix = version > 1 ? String.valueOf(version) : "";
+        return FILE_NAME + infix + FILE_SUFFIX;
+    }
+
+    /**
+     * Returns the crypto algorithm used to sign digest files of the specified version.
+     */
+    public static String sigAlgorithm (int version) {
+        switch (version) {
+        case 1: return "SHA1withRSA";
+        case 2: return "SHA256withRSA";
+        default: throw new IllegalArgumentException("Invalid digest version " + version);
+        }
+    }
+
+    /**
+     * Creates a digest file at the specified location using the supplied list of resources.
+     * @param version the version of the digest protocol to use.
+     */
+    public static void createDigest (int version, List<Resource> resources, File output)
+        throws IOException
+    {
+        // first compute the digests for all the resources in parallel
+        ExecutorService exec = Executors.newFixedThreadPool(SysProps.threadPoolSize());
+        final Map<Resource, String> digests = new ConcurrentHashMap<>();
+        final BlockingQueue<Object> completed = new LinkedBlockingQueue<>();
+        final int fversion = version;
+
+        long start = System.currentTimeMillis();
+
+        Set<Resource> pending = new HashSet<>(resources);
+        for (final Resource rsrc : resources) {
+            exec.execute(new Runnable() {
+                public void run () {
+                    try {
+                        MessageDigest md = getMessageDigest(fversion);
+                        digests.put(rsrc, rsrc.computeDigest(fversion, md, null));
+                        completed.add(rsrc);
+                    } catch (Throwable t) {
+                        completed.add(new IOException("Error computing digest for: " + rsrc).
+                                      initCause(t));
+                    }
+                }
+            });
+        }
+
+        // queue a shutdown of the thread pool when the tasks are done
+        exec.shutdown();
+
+        try {
+            while (pending.size() > 0) {
+                Object done = completed.poll(600, TimeUnit.SECONDS);
+                if (done instanceof IOException) {
+                    throw (IOException)done;
+                } else if (done instanceof Resource) {
+                    pending.remove((Resource)done);
+                } else {
+                    throw new AssertionError("What is this? " + done);
+                }
+            }
+        } catch (InterruptedException ie) {
+            throw new IOException("Timeout computing digests. Wow.");
+        }
+
+        StringBuilder data = new StringBuilder();
+        try (FileOutputStream fos = new FileOutputStream(output);
+             OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
+             PrintWriter pout = new PrintWriter(osw)) {
+            // compute and append the digest of each resource in the list
+            for (Resource rsrc : resources) {
+                String path = rsrc.getPath();
+                String digest = digests.get(rsrc);
+                note(data, path, digest);
+                pout.println(path + " = " + digest);
+            }
+            // finally compute and append the digest for the file contents
+            MessageDigest md = getMessageDigest(version);
+            byte[] contents = data.toString().getBytes(UTF_8);
+            String filename = digestFile(version);
+            pout.println(filename + " = " + StringUtil.hexlate(md.digest(contents)));
+        }
+
+        long elapsed = System.currentTimeMillis() - start;
+        log.debug("Computed digests [rsrcs=" + resources.size() + ", time=" + elapsed + "ms]");
+    }
+
+    /**
+     * Obtains an appropriate message digest instance for use by the Getdown system.
+     */
+    public static MessageDigest getMessageDigest (int version)
+    {
+        String algo = version > 1 ? "SHA-256" : "MD5";
+        try {
+            return MessageDigest.getInstance(algo);
+        } catch (NoSuchAlgorithmException nsae) {
+            throw new RuntimeException("JVM does not support " + algo + ". Gurp!");
+        }
+    }
+
+    /**
+     * Creates a digest instance which will parse and validate the digest in the supplied
+     * application directory, using the current digest version.
+     */
+    public Digest (File appdir, boolean strictComments) throws IOException {
+        this(appdir, VERSION, strictComments);
+    }
+
+    /**
+     * Creates a digest instance which will parse and validate the digest in the supplied
+     * application directory.
+     * @param version the version of the digest protocol to use.
+     */
+    public Digest (File appdir, int version, boolean strictComments) throws IOException
+    {
+        // parse and validate our digest file contents
+        String filename = digestFile(version);
+        StringBuilder data = new StringBuilder();
+        File dfile = new File(appdir, filename);
+        Config.ParseOpts opts = Config.createOpts(false);
+        opts.strictComments = strictComments;
+        // bias = toward key: the key is the filename and could conceivably contain = signs, value
+        // is the hex encoded hash which will not contain =
+        opts.biasToKey = true;
+        for (String[] pair : Config.parsePairs(dfile, opts)) {
+            if (pair[0].equals(filename)) {
+                _metaDigest = pair[1];
+                break;
+            }
+            _digests.put(pair[0], pair[1]);
+            note(data, pair[0], pair[1]);
+        }
+
+        // we've reached the end, validate our contents
+        MessageDigest md = getMessageDigest(version);
+        byte[] contents = data.toString().getBytes(UTF_8);
+        String hash = StringUtil.hexlate(md.digest(contents));
+        if (!hash.equals(_metaDigest)) {
+            String err = MessageUtil.tcompose("m.invalid_digest_file", _metaDigest, hash);
+            throw new IOException(err);
+        }
+    }
+
+    /**
+     * Returns the digest for the digest file.
+     */
+    public String getMetaDigest ()
+    {
+        return _metaDigest;
+    }
+
+    /**
+     * Computes the hash of the specified resource and compares it with the value parsed from
+     * the digest file. Logs a message if the resource fails validation.
+     *
+     * @return true if the resource is valid, false if it failed the digest check or if an I/O
+     * error was encountered during the validation process.
+     */
+    public boolean validateResource (Resource resource, ProgressObserver obs)
+    {
+        try {
+            String chash = resource.computeDigest(VERSION, getMessageDigest(VERSION), obs);
+            String ehash = _digests.get(resource.getPath());
+            if (chash.equals(ehash)) {
+                return true;
+            }
+            log.info("Resource failed digest check",
+                     "rsrc", resource, "computed", chash, "expected", ehash);
+        } catch (Throwable t) {
+            log.info("Resource failed digest check", "rsrc", resource, "error", t);
+        }
+        return false;
+    }
+
+    /**
+     * Returns the digest of the given {@code resource}.
+     */
+    public String getDigest (Resource resource)
+    {
+        return _digests.get(resource.getPath());
+    }
+
+    /** Used by {@link #createDigest} and {@link Digest}. */
+    protected static void note (StringBuilder data, String path, String digest)
+    {
+        data.append(path).append(" = ").append(digest).append("\n");
+    }
+
+    protected HashMap<String, String> _digests = new HashMap<>();
+    protected String _metaDigest = "";
+
+    protected static final String FILE_NAME = "digest";
+    protected static final String FILE_SUFFIX = ".txt";
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java
new file mode 100644 (file)
index 0000000..57b8d84
--- /dev/null
@@ -0,0 +1,245 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.*;
+
+import com.threerings.getdown.util.StringUtil;
+import com.threerings.getdown.data.Application;
+
+/** Configuration that comes from our "environment" (command line args, sys props, etc.). */
+public final class EnvConfig {
+
+    /** Used to report problems or feedback by {@link #create}. */
+    public static final class Note {
+        public static enum Level { INFO, WARN, ERROR };
+        public static Note info (String msg) { return new Note(Level.INFO, msg); }
+        public static Note warn (String msg) { return new Note(Level.WARN, msg); }
+        public static Note error (String msg) { return new Note(Level.ERROR, msg); }
+        public final Level level;
+        public final String message;
+        public Note (Level level, String message) {
+            this.level = level;
+            this.message = message;
+        }
+    }
+
+    /**
+     * Creates an environment config, obtaining information (in order) from the following sources:
+     *
+     * <ul>
+     * <li> A {@code bootstrap.properties} file bundled with the jar. </li>
+     * <li> System properties supplied to the JVM. </li>
+     * <li> The supplied command line arguments ({@code argv}). </li>
+     * </ul>
+     *
+     * If a later source supplies a configuration already provided by a prior source, a warning
+     * message will be logged to indicate the conflict, and the prior source will be used.
+     *
+     * @param notes a list into which notes are added, to be logged after the logging system has
+     * been initialized (which cannot happen until the appdir is known). If any {@code ERROR} notes
+     * are included, the app should terminate after reporting them.
+     * @return an env config instance, or {@code null} if no appdir could be located via any
+     * configuration source.
+     */
+    public static EnvConfig create (String[] argv, List<Note> notes) {
+        String appDir = null, appDirProv = null;
+        String appId = null, appIdProv = null;
+        String appBase = null, appBaseProv = null;
+
+        // start with bootstrap.properties config, if avaialble
+        try {
+            ResourceBundle bundle = ResourceBundle.getBundle("bootstrap");
+            if (bundle.containsKey("appdir")) {
+                appDir = bundle.getString("appdir");
+                appDir = appDir.replace(USER_HOME_KEY, System.getProperty("user.home"));
+                appDirProv = "bootstrap.properties";
+            }
+            if (bundle.containsKey("appid")) {
+                appId = bundle.getString("appid");
+                appIdProv = "bootstrap.properties";
+            }
+            if (bundle.containsKey("appbase")) {
+                appBase = bundle.getString("appbase");
+                appBaseProv = "bootstrap.properties";
+            }
+            // if any system properties are specified (keys prefixed with sys.), set those up
+            for (String key : bundle.keySet()) {
+                if (key.startsWith("sys.")) {
+                    String skey = key.substring(4);
+                    String svalue = bundle.getString(key);
+                    notes.add(Note.info("Setting system property from bundle: " +
+                                        skey + "='" + svalue + "'"));
+                    System.setProperty(skey, svalue);
+                }
+            }
+
+        } catch (MissingResourceException e) {
+            // bootstrap.properties is optional; no need for a warning
+        }
+
+        // next seek config from system properties
+        String spropsAppDir = SysProps.appDir();
+        if (!StringUtil.isBlank(spropsAppDir)) {
+            if (appDir == null) {
+                appDir = spropsAppDir;
+                appDirProv = "system property";
+            } else {
+                notes.add(Note.warn("Ignoring 'appdir' system property, have appdir via '" +
+                                    appDirProv + "'"));
+            }
+        }
+        String spropsAppId = SysProps.appId();
+        if (!StringUtil.isBlank(spropsAppId)) {
+            if (appId == null) {
+                appId = spropsAppId;
+                appIdProv = "system property";
+            } else {
+                notes.add(Note.warn("Ignoring 'appid' system property, have appid via '" +
+                                    appIdProv + "'"));
+            }
+        }
+        String spropsAppBase = SysProps.appBase();
+        if (!StringUtil.isBlank(spropsAppBase)) {
+            if (appBase == null) {
+                appBase = spropsAppBase;
+                appBaseProv = "system property";
+            } else {
+                notes.add(Note.warn("Ignoring 'appbase' system property, have appbase via '" +
+                                    appBaseProv + "'"));
+            }
+        }
+
+        // finally obtain config from command line arguments
+        String argvAppDir = argv.length > 0 ? argv[0] : null;
+        if (!StringUtil.isBlank(argvAppDir)) {
+            if (appDir == null) {
+                appDir = argvAppDir;
+                appDirProv = "command line";
+            } else {
+                notes.add(Note.warn("Ignoring 'appdir' command line arg, have appdir via '" +
+                                    appDirProv + "'"));
+            }
+        }
+        String argvAppId = argv.length > 1 ? argv[1] : null;
+        if (!StringUtil.isBlank(argvAppId)) {
+            if (appId == null) {
+                appId = argvAppId;
+                appIdProv = "command line";
+            } else {
+                notes.add(Note.warn("Ignoring 'appid' command line arg, have appid via '" +
+                                    appIdProv + "'"));
+            }
+        }
+        
+        int skipArgs = 2;
+        // Look for locator file, pass to Application and remove from appArgs
+        String argvLocatorFilename = argv.length > 2 ? argv[2] : null;
+        if (
+                !StringUtil.isBlank(argvLocatorFilename)
+                && argvLocatorFilename.toLowerCase().endsWith("."+Application.LOCATOR_FILE_EXTENSION)
+                ) {
+          notes.add(Note.info("locatorFilename in args: '"+argv[2]+"'"));
+          Application.setLocatorFile(argvLocatorFilename);
+          
+          skipArgs++;
+        }
+
+        // ensure that we were able to find an app dir
+        if (appDir == null) {
+            return null; // caller will report problem to user
+        }
+
+        notes.add(Note.info("Using appdir from " + appDirProv + ": " + appDir));
+        if (appId != null) notes.add(Note.info("Using appid from " + appIdProv + ": " + appId));
+        if (appBase != null) notes.add(
+            Note.info("Using appbase from " + appBaseProv + ": " + appBase));
+
+        // ensure that the appdir refers to a directory that exists
+        File appDirFile = new File(appDir);
+        if (!appDirFile.exists()) {
+            // if we have a bootstrap URL then we auto-create the app dir; this enables an
+            // installer to simply place a getdown.jar file somewhere and create an OS shortcut
+            // that runs getdown with an appdir and appbase specified, and have getdown create the
+            // appdir and download the app into it
+            if (!StringUtil.isBlank(appBase)) {
+                if (appDirFile.mkdirs()) {
+                    notes.add(Note.info("Auto-created app directory '" + appDir + "'"));
+                } else {
+                    notes.add(Note.warn("Unable to auto-create app dir: '" + appDir + "'"));
+                }
+            } else {
+                notes.add(Note.error("Invalid appdir '" + appDir + "': directory does not exist"));
+                return null;
+            }
+        } else if (!appDirFile.isDirectory()) {
+            notes.add(Note.error("Invalid appdir '" + appDir + "': refers to non-directory"));
+            return null;
+        }
+
+        // pass along anything after the first two (or three) args as extra app args
+        List<String> appArgs = argv.length > skipArgs ?
+            Arrays.asList(argv).subList(skipArgs, argv.length) :
+            Collections.<String>emptyList();
+
+        // load X.509 certificate if it exists
+        File crtFile = new File(appDirFile, Digest.digestFile(Digest.VERSION) + ".crt");
+        List<Certificate> certs = new ArrayList<>();
+        if (crtFile.exists()) {
+            try (FileInputStream fis = new FileInputStream(crtFile)) {
+                X509Certificate certificate = (X509Certificate)
+                    CertificateFactory.getInstance("X.509").generateCertificate(fis);
+                certs.add(certificate);
+            } catch (Exception e) {
+                notes.add(Note.error("Certificate error: " + e.getMessage()));
+            }
+        }
+
+        return new EnvConfig(appDirFile, appId, appBase, certs, appArgs);
+    }
+
+    /** The directory in which the application and metadata is stored. */
+    public final File appDir;
+
+    /** Either {@code null} or an identifier for a secondary application that should be
+      * launched. That app will use {@code appid.class} and {@code appid.apparg} to configure
+      * itself but all other parameters will be the same as the primary app. */
+    public final String appId;
+
+    /** Either {@code null} or fallback {@code appbase} to use if one cannot be read from a
+      * {@code getdown.txt} file during startup. */
+    public final String appBase;
+
+    /** Zero or more signing certificates used to verify the digest file. */
+    public final List<Certificate> certs;
+
+    /** Additional arguments to pass on to launched application. These will be added after the
+      * args in the getdown.txt file. */
+    public final List<String> appArgs;
+
+    public EnvConfig (File appDir) {
+        this(appDir, null, null, Collections.<Certificate>emptyList(),
+             Collections.<String>emptyList());
+    }
+
+    private EnvConfig (File appDir, String appId, String appBase, List<Certificate> certs,
+                       List<String> appArgs) {
+        this.appDir = appDir;
+        this.appId = appId;
+        this.appBase = appBase;
+        this.certs = certs;
+        this.appArgs = appArgs;
+    }
+
+    private static final String USER_HOME_KEY = "${user.home}";
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/PathBuilder.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/PathBuilder.java
new file mode 100644 (file)
index 0000000..b0a1dc9
--- /dev/null
@@ -0,0 +1,135 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.jar.JarFile;
+
+import com.threerings.getdown.cache.GarbageCollector;
+import com.threerings.getdown.cache.ResourceCache;
+import com.threerings.getdown.util.FileUtil;
+import static com.threerings.getdown.Log.log;
+
+public class PathBuilder
+{
+    /** Name of directory to store cached code files in. */
+    public static final String CODE_CACHE_DIR = ".cache";
+
+    /** Name of directory to store cached native resources in. */
+    public static final String NATIVE_CACHE_DIR = ".ncache";
+
+    /**
+     * Builds either a default or cached classpath based on {@code app}'s configuration.
+     */
+    public static ClassPath buildClassPath (Application app) throws IOException
+    {
+        return app.useCodeCache() ? buildCachedClassPath(app) : buildDefaultClassPath(app);
+    }
+
+    /**
+     * Builds a {@link ClassPath} instance for {@code app} using the code resources in place in
+     * the app directory.
+     */
+    public static ClassPath buildDefaultClassPath (Application app)
+    {
+        LinkedHashSet<File> classPathEntries = new LinkedHashSet<File>();
+        for (Resource resource: app.getActiveCodeResources()) {
+            classPathEntries.add(resource.getFinalTarget());
+        }
+        return new ClassPath(classPathEntries);
+    }
+
+    /**
+     * Builds a {@link ClassPath} instance for {@code app} by first copying the code resources into
+     * a cache directory and then referencing them from there. This avoids problems with
+     * overwriting in-use classpath elements when the application is later updated. This also
+     * "garbage collects" expired caches if necessary.
+     */
+    public static ClassPath buildCachedClassPath (Application app) throws IOException
+    {
+        File codeCacheDir = new File(app.getAppDir(), CODE_CACHE_DIR);
+
+        // a negative value of code_cache_retention_days allows to clean up the cache forcefully
+        long retainMillis = TimeUnit.DAYS.toMillis(app.getCodeCacheRetentionDays());
+        if (retainMillis != 0L) {
+            GarbageCollector.collect(codeCacheDir, retainMillis);
+        }
+
+        ResourceCache cache = new ResourceCache(codeCacheDir);
+        LinkedHashSet<File> classPathEntries = new LinkedHashSet<>();
+        for (Resource resource: app.getActiveCodeResources()) {
+            String digest = app.getDigest(resource);
+            File entry = cache.cacheFile(resource.getFinalTarget(), digest.substring(0, 2), digest);
+            classPathEntries.add(entry);
+        }
+
+        return new ClassPath(classPathEntries);
+    }
+
+    /**
+     * Builds a {@link ClassPath} instance by first caching all native jars (indicated by
+     * nresource=[native jar]), unpacking them, and referencing the locations of each of the
+     * unpacked files. Also performs garbage collection similar to {@link #buildCachedClassPath}
+     *
+     * @param app                   used to determine native jars and related information.
+     * @param addCurrentLibraryPath if true, it adds the locations referenced by
+     *                              {@code System.getProperty("java.library.path")} as well.
+     * @return a classpath instance if at least one native resource was found and unpacked,
+     *         {@code null} if no native resources were used by the application.
+     */
+    public static ClassPath buildLibsPath (Application app,
+                                           boolean addCurrentLibraryPath) throws IOException {
+        List<Resource> resources = app.getNativeResources();
+        if (resources.isEmpty()) {
+            return null;
+        }
+
+        LinkedHashSet<File> nativedirs = new LinkedHashSet<>();
+        File nativeCacheDir = new File(app.getAppDir(), NATIVE_CACHE_DIR);
+        ResourceCache cache = new ResourceCache(nativeCacheDir);
+
+        // negative value forces total garbage collection, 0 avoids garbage collection at all
+        long retainMillis = TimeUnit.DAYS.toMillis(app.getCodeCacheRetentionDays());
+        if (retainMillis != 0L) {
+            GarbageCollector.collectNative(nativeCacheDir, retainMillis);
+        }
+
+        for (Resource resource : resources) {
+            // Use untruncated cache subdirectory names to avoid overwriting issues when unpacking,
+            // in the off chance that two native jars share a directory AND contain files with the
+            // same names
+            String digest = app.getDigest(resource);
+            File cachedFile = cache.cacheFile(resource.getFinalTarget(), digest, digest);
+            File cachedParent = cachedFile.getParentFile();
+            File unpackedIndicator = new File(cachedParent, cachedFile.getName() + ".unpacked");
+
+            if (!unpackedIndicator.exists()) {
+                try {
+                    FileUtil.unpackJar(new JarFile(cachedFile), cachedParent, false);
+                    unpackedIndicator.createNewFile();
+                } catch (IOException ioe) {
+                    log.warning("Failed to unpack native jar",
+                                "file", cachedFile.getAbsolutePath(), ioe);
+                    // Keep going and unpack the other jars...
+                }
+            }
+
+            nativedirs.add(cachedFile.getParentFile());
+        }
+
+        if (addCurrentLibraryPath) {
+            for (String path : System.getProperty("java.library.path").split(File.pathSeparator)) {
+                nativedirs.add(new File(path));
+            }
+        }
+
+        return new ClassPath(nativedirs);
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Properties.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Properties.java
new file mode 100644 (file)
index 0000000..e70bd4b
--- /dev/null
@@ -0,0 +1,19 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+/**
+ * System property constants associated with Getdown.
+ */
+public class Properties
+{
+    /** This property will be set to "true" on the application when it is being run by getdown. */
+    public static final String GETDOWN = "com.threerings.getdown";
+
+    /** If accepting connections from the launched application, this property
+     * will be set to the connection server port. */
+    public static final String CONNECT_PORT = "com.threerings.getdown.connectPort";
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Resource.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Resource.java
new file mode 100644 (file)
index 0000000..adc2d4f
--- /dev/null
@@ -0,0 +1,415 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.io.*;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
+
+import com.threerings.getdown.util.FileUtil;
+import com.threerings.getdown.util.ProgressObserver;
+import com.threerings.getdown.util.StringUtil;
+
+import static com.threerings.getdown.Log.log;
+
+/**
+ * Models a single file resource used by an {@link Application}.
+ */
+public class Resource implements Comparable<Resource>
+{
+    /** Defines special attributes for resources. */
+    public static enum Attr {
+        /** Indicates that the resource should be unpacked. */
+        UNPACK,
+        /** If present, when unpacking a resource, any directories created by the newly
+          * unpacked resource will first be cleared of files before unpacking. */
+        CLEAN,
+        /** Indicates that the resource should be marked executable. */
+        EXEC,
+        /** Indicates that the resource should be downloaded before a UI is displayed. */
+        PRELOAD,
+        /** Indicates that the resource is a jar containing native libs. */
+        NATIVE
+    };
+
+    public static final EnumSet<Attr> NORMAL  = EnumSet.noneOf(Attr.class);
+    public static final EnumSet<Attr> UNPACK  = EnumSet.of(Attr.UNPACK);
+    public static final EnumSet<Attr> EXEC    = EnumSet.of(Attr.EXEC);
+    public static final EnumSet<Attr> PRELOAD = EnumSet.of(Attr.PRELOAD);
+    public static final EnumSet<Attr> NATIVE  = EnumSet.of(Attr.NATIVE);
+
+    /**
+     * Computes the MD5 hash of the supplied file.
+     * @param version the version of the digest protocol to use.
+     */
+    public static String computeDigest (int version, File target, MessageDigest md,
+                                        ProgressObserver obs)
+        throws IOException
+    {
+        md.reset();
+        byte[] buffer = new byte[DIGEST_BUFFER_SIZE];
+        int read;
+
+        boolean isJar = isJar(target.getPath());
+        boolean isPacked200Jar = isPacked200Jar(target.getPath());
+
+        // if this is a jar, we need to compute the digest in a "timestamp and file order" agnostic
+        // manner to properly correlate jardiff patched jars with their unpatched originals
+        if (isJar || isPacked200Jar){
+            File tmpJarFile = null;
+            JarFile jar = null;
+            try {
+                // if this is a compressed jar file, uncompress it to compute the jar file digest
+                if (isPacked200Jar){
+                    tmpJarFile = new File(target.getPath() + ".tmp");
+                    FileUtil.unpackPacked200Jar(target, tmpJarFile);
+                    jar = new JarFile(tmpJarFile);
+                } else{
+                    jar = new JarFile(target);
+                }
+
+                List<JarEntry> entries = Collections.list(jar.entries());
+                Collections.sort(entries, ENTRY_COMP);
+
+                int eidx = 0;
+                for (JarEntry entry : entries) {
+                    // old versions of the digest code skipped metadata
+                    if (version < 2) {
+                        if (entry.getName().startsWith("META-INF")) {
+                            updateProgress(obs, eidx, entries.size());
+                            continue;
+                        }
+                    }
+
+                    try (InputStream in = jar.getInputStream(entry)) {
+                        while ((read = in.read(buffer)) != -1) {
+                            md.update(buffer, 0, read);
+                        }
+                    }
+
+                    updateProgress(obs, eidx, entries.size());
+                }
+
+            } finally {
+                if (jar != null) {
+                    try {
+                        jar.close();
+                    } catch (IOException ioe) {
+                        log.warning("Error closing jar", "path", target, "jar", jar, "error", ioe);
+                    }
+                }
+                if (tmpJarFile != null) {
+                    FileUtil.deleteHarder(tmpJarFile);
+                }
+            }
+
+        } else {
+            long totalSize = target.length(), position = 0L;
+            try (FileInputStream fin = new FileInputStream(target)) {
+                while ((read = fin.read(buffer)) != -1) {
+                    md.update(buffer, 0, read);
+                    position += read;
+                    updateProgress(obs, position, totalSize);
+                }
+            }
+        }
+        return StringUtil.hexlate(md.digest());
+    }
+
+    /**
+     * Creates a resource with the supplied remote URL and local path.
+     */
+    public Resource (String path, URL remote, File local, EnumSet<Attr> attrs)
+    {
+        _path = path;
+        _remote = remote;
+        _local = local;
+        _localNew = new File(local.toString() + "_new");
+        String lpath = _local.getPath();
+        _marker = new File(lpath + "v");
+
+        _attrs = attrs;
+        _isTgz = isTgz(lpath);
+        _isJar = isJar(lpath);
+        _isPacked200Jar = isPacked200Jar(lpath);
+        boolean unpack = attrs.contains(Attr.UNPACK);
+        if (unpack && _isJar) {
+            _unpacked = _local.getParentFile();
+        } else if(unpack && _isTgz) {
+            _unpacked = _local.getParentFile();
+        } else if(unpack && _isPacked200Jar) {
+            String dotJar = ".jar", lname = _local.getName();
+            String uname = lname.substring(0, lname.lastIndexOf(dotJar) + dotJar.length());
+            _unpacked = new File(_local.getParent(), uname);
+        }
+    }
+
+    /**
+     * Returns the path associated with this resource.
+     */
+    public String getPath ()
+    {
+        return _path;
+    }
+
+    /**
+     * Returns the local location of this resource.
+     */
+    public File getLocal ()
+    {
+        return _local;
+    }
+
+    /**
+     * Returns the location of the to-be-installed new version of this resource.
+     */
+    public File getLocalNew ()
+    {
+        return _localNew;
+    }
+
+    /**
+     *  Returns the location of the unpacked resource.
+     */
+    public File getUnpacked ()
+    {
+        return _unpacked;
+    }
+
+    /**
+     *  Returns the final target of this resource, whether it has been unpacked or not.
+     */
+    public File getFinalTarget ()
+    {
+        return shouldUnpack() ? getUnpacked() : getLocal();
+    }
+
+    /**
+     * Returns the remote location of this resource.
+     */
+    public URL getRemote ()
+    {
+        return _remote;
+    }
+
+    /**
+     * Returns true if this resource should be unpacked as a part of the validation process.
+     */
+    public boolean shouldUnpack ()
+    {
+        return _attrs.contains(Attr.UNPACK) && !SysProps.noUnpack();
+    }
+
+    /**
+     * Returns true if this resource should be pre-downloaded.
+     */
+    public boolean shouldPredownload ()
+    {
+        return _attrs.contains(Attr.PRELOAD);
+    }
+
+    /**
+     * Returns true if this resource is a native lib jar.
+     */
+    public boolean isNative ()
+    {
+        return _attrs.contains(Attr.NATIVE);
+    }
+
+    /**
+     * Computes the MD5 hash of this resource's underlying file.
+     * <em>Note:</em> This is both CPU and I/O intensive.
+     * @param version the version of the digest protocol to use.
+     */
+    public String computeDigest (int version, MessageDigest md, ProgressObserver obs)
+        throws IOException
+    {
+        File file;
+        if (_local.toString().toLowerCase(Locale.ROOT).endsWith(Application.CONFIG_FILE)) {
+            file = _local;
+        } else {
+            file = _localNew.exists() ? _localNew : _local;
+        }
+        return computeDigest(version, file, md, obs);
+    }
+
+    /**
+     * Returns true if this resource has an associated "validated" marker
+     * file.
+     */
+    public boolean isMarkedValid ()
+    {
+        if (!_local.exists()) {
+            clearMarker();
+            return false;
+        }
+        return _marker.exists();
+    }
+
+    /**
+     * Creates a "validated" marker file for this resource to indicate
+     * that its MD5 hash has been computed and compared with the value in
+     * the digest file.
+     *
+     * @throws IOException if we fail to create the marker file.
+     */
+    public void markAsValid ()
+        throws IOException
+    {
+        _marker.createNewFile();
+    }
+
+    /**
+     * Removes any "validated" marker file associated with this resource.
+     */
+    public void clearMarker ()
+    {
+        if (_marker.exists() && !FileUtil.deleteHarder(_marker)) {
+            log.warning("Failed to erase marker file '" + _marker + "'.");
+        }
+    }
+
+    /**
+     * Installs the {@code getLocalNew} version of this resource to {@code getLocal}.
+     * @param validate whether or not to mark the resource as valid after installing.
+     */
+    public void install (boolean validate) throws IOException {
+        File source = getLocalNew(), dest = getLocal();
+        log.info("- " + source);
+        if (!FileUtil.renameTo(source, dest)) {
+            throw new IOException("Failed to rename " + source + " to " + dest);
+        }
+        applyAttrs();
+        if (validate) {
+            markAsValid();
+        }
+    }
+
+    /**
+     * Unpacks this resource file into the directory that contains it.
+     */
+    public void unpack () throws IOException
+    {
+        // sanity check
+        if (!_isJar && !_isPacked200Jar && !_isTgz) {
+            throw new IOException("Requested to unpack non-jar/tgz file '" + _local + "'.");
+        }
+        if (_isJar) {
+            try (JarFile jar = new JarFile(_local)) {
+                FileUtil.unpackJar(jar, _unpacked, _attrs.contains(Attr.CLEAN));
+            }
+        } else if (_isTgz) {
+            try (InputStream fi = Files.newInputStream(_local.toPath());
+                         InputStream bi = new BufferedInputStream(fi);
+                         InputStream gzi = new GzipCompressorInputStream(bi);
+                         TarArchiveInputStream tgz = new TarArchiveInputStream(gzi)) {
+                    FileUtil.unpackTgz(tgz, _unpacked, _attrs.contains(Attr.CLEAN));
+            }
+        } else {
+            FileUtil.unpackPacked200Jar(_local, _unpacked);
+        }
+    }
+
+    /**
+     * Applies this resources special attributes: unpacks this resource if needed, marks it as
+     * executable if needed.
+     */
+    public void applyAttrs () throws IOException {
+        if (shouldUnpack()) {
+            unpack();
+        }
+        if (_attrs.contains(Attr.EXEC)) {
+            FileUtil.makeExecutable(_local);
+        }
+    }
+
+    /**
+     * Wipes this resource file along with any "validated" marker file that may be associated with
+     * it.
+     */
+    public void erase ()
+    {
+        clearMarker();
+        if (_local.exists() && !FileUtil.deleteHarder(_local)) {
+            log.warning("Failed to erase resource '" + _local + "'.");
+        }
+    }
+
+    @Override public int compareTo (Resource other) {
+        return _path.compareTo(other._path);
+    }
+
+    @Override public boolean equals (Object other)
+    {
+        if (other instanceof Resource) {
+            return _path.equals(((Resource)other)._path);
+        } else {
+            return false;
+        }
+    }
+
+    @Override public int hashCode ()
+    {
+        return _path.hashCode();
+    }
+
+    @Override public String toString ()
+    {
+        return _path;
+    }
+
+    /** Helper function to simplify the process of reporting progress. */
+    protected static void updateProgress (ProgressObserver obs, long pos, long total)
+    {
+        if (obs != null) {
+            obs.progress((int)(100 * pos / total));
+        }
+    }
+
+    protected static boolean isJar (String path)
+    {
+        return path.endsWith(".jar") || path.endsWith(".jar_new");
+    }
+    
+    protected static boolean isTgz (String path)
+    {
+        return path.endsWith(".tgz") || path.endsWith(".tgz_new");
+    }
+
+    protected static boolean isPacked200Jar (String path)
+    {
+        return path.endsWith(".jar.pack") || path.endsWith(".jar.pack_new") ||
+            path.endsWith(".jar.pack.gz")|| path.endsWith(".jar.pack.gz_new");
+    }
+
+    protected String _path;
+    protected URL _remote;
+    protected File _local, _localNew, _marker, _unpacked;
+    protected EnumSet<Attr> _attrs;
+    protected boolean _isJar, _isPacked200Jar, _isTgz;
+
+    /** Used to sort the entries in a jar file. */
+    protected static final Comparator<JarEntry> ENTRY_COMP = new Comparator<JarEntry>() {
+        @Override public int compare (JarEntry e1, JarEntry e2) {
+            return e1.getName().compareTo(e2.getName());
+        }
+    };
+
+    protected static final int DIGEST_BUFFER_SIZE = 5 * 1025;
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/SysProps.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/SysProps.java
new file mode 100644 (file)
index 0000000..8550461
--- /dev/null
@@ -0,0 +1,186 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.threerings.getdown.util.VersionUtil;
+import com.threerings.getdown.data.Build;
+
+/**
+ * This class encapsulates all system properties that are read and processed by Getdown. Don't
+ * stick a call to {@code System.getProperty} randomly into the code, put it in here and give it an
+ * accessor so that it's easy to see all of the secret system property arguments that Getdown makes
+ * use of.
+ */
+public class SysProps
+{
+    /** Configures the appdir (in lieu of passing it in argv). Usage: {@code -Dappdir=foo}. */
+    public static String appDir () {
+        return System.getProperty("appdir");
+    }
+
+    /** Configures the appid (in lieu of passing it in argv). Usage: {@code -Dappid=foo}. */
+    public static String appId () {
+        return System.getProperty("appid");
+    }
+
+    /** Configures the bootstrap appbase (used in lieu of providing a skeleton getdown.txt, and as
+      * a last resort fallback). Usage: {@code -Dappbase=URL}. */
+    public static String appBase () {
+        return System.getProperty("appbase");
+    }
+
+    /** If true, disables redirection of logging into {@code launcher.log}.
+      * Usage: {@code -Dno_log_redir}. */
+    public static boolean noLogRedir () {
+        return System.getProperty("no_log_redir") != null;
+    }
+
+    /** Overrides the domain on {@code appbase}. Usage: {@code -Dappbase_domain=foo}. */
+    public static String appbaseDomain () {
+        return System.getProperty("appbase_domain");
+    }
+
+    /** Overrides enter {@code appbase}. Usage: {@code -Dappbase_override=URL}. */
+    public static String appbaseOverride () {
+        return System.getProperty("appbase_override");
+    }
+
+    /** If true, Getdown installs the app without ever bringing up a UI (except in the event of an
+      * error). NOTE: it does not launch the app. See {@link #launchInSilent}.
+      * Usage: {@code -Dsilent}. */
+    public static boolean silent () {
+        return System.getProperty("silent") != null;
+    }
+
+    /** Instructs Getdown to install/update the app without ever bringing up a UI (except in the
+      * event of an error), and then launch it.
+      * Usage: {@code -Dsilent=launch}. */
+    public static boolean launchInSilent () {
+        return "launch".equals(System.getProperty("silent"));
+    }
+
+    /**
+     * Instructs Getdown to launch the app without updating it, or ever bringing up a UI (except
+     * in the event of an error).
+     * Usage: {@code -Dsilent=noupdate}.
+     */
+    public static boolean noUpdate() {
+        return "noupdate".equals(System.getProperty("silent"));
+    }
+
+    /** If true, Getdown does not automatically install updates after downloading them. It waits
+      * for the application to call `Getdown.install`.
+      * Usage: {@code -Dno_install}. */
+    public static boolean noInstall () {
+        return System.getProperty("no_install") != null;
+    }
+
+    /** Specifies the delay (in minutes) to wait before starting the update and install process.
+      * Minimum delay is 0 minutes, or no delay (negative values are rounded up to 0 minutes).
+      * Maximum delay is 1 day, or 1440 minutes (larger values are rounded down to 1 day).
+      * Usage: {@code -Ddelay=N}. */
+    public static int startDelay () {
+        return Math.min(Math.max(Integer.getInteger("delay", 0), 0), 60 * 24);
+    }
+
+    /** If true, Getdown will not unpack {@code uresource} jars. Usage: {@code -Dno_unpack}. */
+    public static boolean noUnpack () {
+        return Boolean.getBoolean("no_unpack");
+    }
+
+    /** If true, Getdown will run the application in the same VM in which Getdown is running. If
+      * false (the default), Getdown will fork a new VM. Note that reusing the same VM prevents
+      * Getdown from configuring some launch-time-only VM parameters (like -mxN etc.).
+      * Usage: {@code -Ddirect}. */
+    public static boolean direct () {
+        return Boolean.getBoolean("direct");
+    }
+
+    /** Specifies the connection timeout (in seconds) to use when downloading control files from
+      * the server. This is chiefly useful when you are running in versionless mode and want Getdown
+      * to more quickly timeout its startup update check if the server with which it is
+      * communicating is not available. Usage: {@code -Dconnect_timeout=N}. */
+    public static int connectTimeout () {
+        return Integer.getInteger("connect_timeout", Build.defaultConnectTimeout());
+    }
+
+    /** Specifies the read timeout (in seconds) to use when downloading all files from the server.
+      * The default is 30 seconds, meaning that if a download stalls for more than 30 seconds, the
+      * update process wil fail. Setting the timeout to zero (or a negative value) will disable it.
+      * Usage: {@code -Dread_timeout=N}. */
+    public static int readTimeout () {
+        return Integer.getInteger("read_timeout", Build.defaultReadTimeout());
+    }
+
+    /** Returns the number of threads used to perform digesting and verifying operations in
+      * parallel. Usage: {@code -Dthread_pool_size=N} */
+    public static int threadPoolSize () {
+        int defaultSize = Math.max(Runtime.getRuntime().availableProcessors()-1, 1);
+        return Integer.getInteger("thread_pool_size", defaultSize);
+    }
+
+    /** Parses a Java version system property using the supplied regular expression. The numbers
+      * extracted from the regexp will be placed in each consecutive hundreds position in the
+      * returned value.
+      *
+      * <p>For example, {@code java.version} takes the form {@code 1.8.0_31}, and with the regexp
+      * {@code (\d+)\.(\d+)\.(\d+)(_\d+)?} we would parse {@code 1, 8, 0, 31} and combine them into
+      * the final value {@code 1080031}.
+      *
+      * <p>Note that non-numeric characters matched by the regular expression will simply be
+      * ignored, and optional groups which do not match are treated as zero in the final version
+      * calculation.
+      *
+      * <p>One can instead parse {@code java.runtime.version} which takes the form {@code
+      * 1.8.0_31-b13}. Using regexp {@code (\d+)\.(\d+)\.(\d+)_(\d+)-b(\d+)} we would parse
+      * {@code 1, 8, 0, 31, 13} and combine them into a final value {@code 108003113}.
+      *
+      * <p>Other (or future) JVMs may provide different version properties which can be parsed as
+      * desired using this general scheme as long as the numbers appear from left to right in order
+      * of significance.
+      *
+      * @throws IllegalArgumentException if no system named {@code propName} exists, or if
+      * {@code propRegex} does not match the returned version string.
+      */
+    public static long parseJavaVersion (String propName, String propRegex) {
+        String verstr = System.getProperty(propName);
+        if (verstr == null) throw new IllegalArgumentException(
+            "No system property '" + propName + "'.");
+
+        long vers = VersionUtil.parseJavaVersion(propRegex, verstr);
+        if (vers == 0L) throw new IllegalArgumentException(
+            "Regexp '" + propRegex + "' does not match '" + verstr + "' (from " + propName + ")");
+        return vers;
+    }
+
+    /**
+     * Applies {@code appbase_override} or {@code appbase_domain} if they are set.
+     */
+    public static String overrideAppbase (String appbase) {
+        String appbaseOverride = appbaseOverride();
+        if (appbaseOverride != null) {
+            return appbaseOverride;
+        } else {
+            return replaceDomain(appbase);
+        }
+    }
+
+    /**
+     * If appbase_domain property is set, replace the domain on the provided string.
+     */
+    public static String replaceDomain (String appbase)
+    {
+        String appbaseDomain = appbaseDomain();
+        if (appbaseDomain != null) {
+            Matcher m = Pattern.compile("(https?://[^/]+)(.*)").matcher(appbase);
+            appbase = m.replaceAll(appbaseDomain + "$2");
+        }
+        return appbase;
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/Downloader.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/Downloader.java
new file mode 100644 (file)
index 0000000..6033e2f
--- /dev/null
@@ -0,0 +1,229 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.net;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import com.threerings.getdown.data.Resource;
+
+import static com.threerings.getdown.Log.log;
+
+/**
+ * Handles the download of a collection of files, first issuing HTTP head requests to obtain size
+ * information and then downloading the files individually, reporting progress back via protected
+ * callback methods. <em>Note:</em> these methods are all called arbitrary download threads, so
+ * implementors must take care to only execute thread-safe code or simply pass a message to the AWT
+ * thread, for example.
+ */
+public abstract class Downloader
+{
+    /**
+     * Start the downloading process.
+     * @param resources the resources to download.
+     * @param maxConcurrent the maximum number of concurrent downloads allowed.
+     * @return true if the download completed, false if it was aborted (via {@link #abort}).
+     */
+    public boolean download (Collection<Resource> resources, int maxConcurrent)
+    {
+        // first compute the total size of our download
+        resolvingDownloads();
+        for (Resource rsrc : resources) {
+            try {
+                _sizes.put(rsrc, Math.max(checkSize(rsrc), 0L));
+            } catch (IOException ioe) {
+                downloadFailed(rsrc, ioe);
+            }
+        }
+
+        long totalSize = sum(_sizes.values());
+        log.info("Downloading " + resources.size() + " resources",
+                 "totalBytes", totalSize, "maxConcurrent", maxConcurrent);
+
+        // make a note of the time at which we started the download
+        _start = System.currentTimeMillis();
+
+        // start the downloads
+        ExecutorService exec = Executors.newFixedThreadPool(maxConcurrent);
+        for (final Resource rsrc : resources) {
+            // make sure the resource's target directory exists
+            File parent = new File(rsrc.getLocal().getParent());
+            if (!parent.exists() && !parent.mkdirs()) {
+                log.warning("Failed to create target directory for resource '" + rsrc + "'.");
+            }
+
+            exec.execute(new Runnable() {
+                @Override public void run () {
+                    try {
+                        if (_state != State.ABORTED) {
+                            download(rsrc);
+                        }
+                    } catch (IOException ioe) {
+                        _state = State.FAILED;
+                        downloadFailed(rsrc, ioe);
+                    }
+                }
+            });
+        }
+        exec.shutdown();
+
+        // wait for the downloads to complete
+        try {
+            exec.awaitTermination(10, TimeUnit.DAYS);
+
+            // report download completion if we did not already do so via our final resource
+            if (_state == State.DOWNLOADING) {
+                downloadProgress(100, 0);
+            }
+
+        } catch (InterruptedException ie) {
+            exec.shutdownNow();
+            downloadFailed(null, ie);
+        }
+
+        return _state != State.ABORTED;
+    }
+
+    /**
+     * Aborts the in-progress download.
+     */
+    public void abort () {
+        _state = State.ABORTED;
+    }
+
+    /**
+     * Called before the downloader begins the series of HTTP head requests to determine the
+     * size of the files it needs to download.
+     */
+    protected void resolvingDownloads () {}
+
+    /**
+     * Reports ongoing progress toward completion of the overall downloading task. One call is
+     * guaranteed to be made reporting 100% completion if the download is not aborted and no
+     * resources fail.
+     *
+     * @param percent the percent completion of the complete download process (based on total bytes
+     * downloaded versus total byte size of all resources).
+     * @param remaining the estimated download time remaining in seconds, or {@code -1} if the time
+     * can not yet be determined.
+     */
+    protected void downloadProgress (int percent, long remaining) {}
+
+    /**
+     * Called if a failure occurs while downloading a resource. No progress will be reported after
+     * a download fails, but additional download failures may be reported.
+     *
+     * @param rsrc the resource that failed to download, or null if the download failed due to
+     * thread interruption.
+     * @param cause the exception detailing the failure.
+     */
+    protected void downloadFailed (Resource rsrc, Exception cause) {}
+
+    /**
+     * Performs the protocol-specific portion of checking download size.
+     */
+    protected abstract long checkSize (Resource rsrc) throws IOException;
+
+    /**
+     * Periodically called by the protocol-specific downloaders to update their progress. This
+     * should be called at least once for each resource to be downloaded, with the total downloaded
+     * size for that resource. It can also be called periodically along the way for each resource
+     * to communicate incremental progress.
+     *
+     * @param rsrc the resource currently being downloaded.
+     * @param currentSize the number of bytes currently downloaded for said resource.
+     * @param actualSize the size reported for this resource now that we're actually downloading
+     * it. Some web servers lie about Content-length when doing a HEAD request, so by reporting
+     * updated sizes here we can recover from receiving bogus information in the earlier
+     * {@link #checkSize} phase.
+     */
+    protected synchronized void reportProgress (Resource rsrc, long currentSize, long actualSize)
+    {
+        // update the actual size for this resource (but don't let it shrink)
+        _sizes.put(rsrc, actualSize = Math.max(actualSize, _sizes.get(rsrc)));
+
+        // update the current downloaded size for said resource; don't allow the downloaded bytes
+        // to exceed the original claimed size of the resource, otherwise our progress will get
+        // booched and we'll end up back on the Daily WTF: http://tinyurl.com/29wt4oq
+        _downloaded.put(rsrc, Math.min(actualSize, currentSize));
+
+        // notify the observer if it's been sufficiently long since our last notification
+        long now = System.currentTimeMillis();
+        if ((now - _lastUpdate) >= UPDATE_DELAY) {
+            _lastUpdate = now;
+
+            // total up our current and total bytes
+            long downloaded = sum(_downloaded.values());
+            long totalSize = sum(_sizes.values());
+
+            // compute our bytes per second
+            long secs = (now - _start) / 1000L;
+            long bps = (secs == 0) ? 0 : (downloaded / secs);
+
+            // compute our percentage completion
+            int pctdone = (totalSize == 0) ? 0 : (int)((downloaded * 100f) / totalSize);
+
+            // estimate our time remaining
+            long remaining = (bps <= 0 || totalSize == 0) ? -1 : (totalSize - downloaded) / bps;
+
+            // if we're complete or failed, when we don't want to report again
+            if (_state == State.DOWNLOADING) {
+                if (pctdone == 100) _state = State.COMPLETE;
+                downloadProgress(pctdone, remaining);
+            }
+        }
+    }
+
+    /**
+     * Sums the supplied values.
+     */
+    protected static long sum (Iterable<Long> values)
+    {
+        long acc = 0L;
+        for (Long value : values) {
+            acc += value;
+        }
+        return acc;
+    }
+
+    protected enum State { DOWNLOADING, COMPLETE, FAILED, ABORTED }
+
+    /**
+     * Accomplishes the copying of the resource from remote location to local location using
+     * protocol-specific code. This method should periodically check whether {@code _state} is set
+     * to aborted and abort any in-progress download if so.
+     */
+    protected abstract void download (Resource rsrc) throws IOException;
+
+    /** The reported sizes of our resources. */
+    protected Map<Resource, Long> _sizes = new HashMap<>();
+
+    /** The bytes downloaded for each resource. */
+    protected Map<Resource, Long> _downloaded = new HashMap<>();
+
+    /** The time at which the file transfer began. */
+    protected long _start;
+
+    /** The current transfer rate in bytes per second. */
+    protected long _bytesPerSecond;
+
+    /** The time at which the last progress update was posted to the progress observer. */
+    protected long _lastUpdate;
+
+    /** A wee state machine to ensure we call our callbacks sanely. */
+    protected volatile State _state = State.DOWNLOADING;
+
+    /** The delay in milliseconds between notifying progress observers of file download
+      * progress. */
+    protected static final long UPDATE_DELAY = 500L;
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java
new file mode 100644 (file)
index 0000000..a7a3287
--- /dev/null
@@ -0,0 +1,115 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.net;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+
+import com.threerings.getdown.data.Resource;
+import com.threerings.getdown.util.ConnectionUtil;
+
+import static com.threerings.getdown.Log.log;
+
+/**
+ * Implements downloading files over HTTP
+ */
+public class HTTPDownloader extends Downloader
+{
+    public HTTPDownloader (Proxy proxy)
+    {
+        _proxy = proxy;
+    }
+
+    @Override protected long checkSize (Resource rsrc) throws IOException
+    {
+        URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0);
+        try {
+            // if we're accessing our data via HTTP, we only need a HEAD request
+            if (conn instanceof HttpURLConnection) {
+                HttpURLConnection hcon = (HttpURLConnection)conn;
+                hcon.setRequestMethod("HEAD");
+                hcon.connect();
+                // make sure we got a satisfactory response code
+                if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) {
+                    throw new IOException("Unable to check up-to-date for " +
+                                          rsrc.getRemote() + ": " + hcon.getResponseCode());
+                }
+            }
+            return conn.getContentLength();
+
+        } finally {
+            // let it be known that we're done with this connection
+            conn.getInputStream().close();
+        }
+    }
+
+    @Override protected void download (Resource rsrc) throws IOException
+    {
+        // TODO: make FileChannel download impl (below) robust and allow apps to opt-into it via a
+        // system property
+        if (true) {
+            // download the resource from the specified URL
+            URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0);
+            conn.connect();
+
+            // make sure we got a satisfactory response code
+            if (conn instanceof HttpURLConnection) {
+                HttpURLConnection hcon = (HttpURLConnection)conn;
+                if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) {
+                    throw new IOException("Unable to download resource " + rsrc.getRemote() + ": " +
+                                          hcon.getResponseCode());
+                }
+            }
+            long actualSize = conn.getContentLength();
+            log.info("Downloading resource", "url", rsrc.getRemote(), "size", actualSize);
+            long currentSize = 0L;
+            byte[] buffer = new byte[4*4096];
+            try (InputStream in = conn.getInputStream();
+                 FileOutputStream out = new FileOutputStream(rsrc.getLocalNew())) {
+
+                // TODO: look to see if we have a download info file
+                // containing info on potentially partially downloaded data;
+                // if so, use a "Range: bytes=HAVE-" header.
+
+                // read in the file data
+                int read;
+                while ((read = in.read(buffer)) != -1) {
+                    // abort the download if the downloader is aborted
+                    if (_state == State.ABORTED) {
+                        break;
+                    }
+                    // write it out to our local copy
+                    out.write(buffer, 0, read);
+                    // note that we've downloaded some data
+                    currentSize += read;
+                    reportProgress(rsrc, currentSize, actualSize);
+                }
+            }
+
+        } else {
+            log.info("Downloading resource", "url", rsrc.getRemote(), "size", "unknown");
+            File localNew = rsrc.getLocalNew();
+            try (ReadableByteChannel rbc = Channels.newChannel(rsrc.getRemote().openStream());
+                 FileOutputStream fos = new FileOutputStream(localNew)) {
+                // TODO: more work is needed here, transferFrom can fail to transfer the entire
+                // file, in which case it's not clear what we're supposed to do.. call it again?
+                // will it repeatedly fail?
+                fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+                reportProgress(rsrc, localNew.length(), localNew.length());
+            }
+        }
+    }
+
+    protected final Proxy _proxy;
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java
new file mode 100644 (file)
index 0000000..22446ec
--- /dev/null
@@ -0,0 +1,32 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.spi;
+
+/**
+ * A service provider interface that handles the storage of proxy credentials.
+ */
+public interface ProxyAuth
+{
+    /** Credentials for a proxy server. */
+    public static class Credentials {
+        public final String username;
+        public final String password;
+        public Credentials (String username, String password) {
+            this.username = username;
+            this.password = password;
+        }
+    }
+
+    /**
+     * Loads the credentials for the app installed in {@code appDir}.
+     */
+    public Credentials loadCredentials (String appDir);
+
+    /**
+     * Encrypts and saves the credentials for the app installed in {@code appDir}.
+     */
+    public void saveCredentials (String appDir, String username, String password);
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Differ.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Differ.java
new file mode 100644 (file)
index 0000000..c2e740b
--- /dev/null
@@ -0,0 +1,232 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.tools;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.zip.ZipEntry;
+
+import java.security.MessageDigest;
+
+import com.threerings.getdown.data.Application;
+import com.threerings.getdown.data.Digest;
+import com.threerings.getdown.data.EnvConfig;
+import com.threerings.getdown.data.Resource;
+import com.threerings.getdown.util.FileUtil;
+import com.threerings.getdown.util.StreamUtil;
+
+/**
+ * Generates patch files between two particular revisions of an
+ * application. The differences between all the files in the two
+ * revisions are bundled into a single patch file which is placed into the
+ * target version directory.
+ */
+public class Differ
+{
+    /**
+     * Creates a single patch file that contains the differences between
+     * the two specified application directories. The patch file will be
+     * created in the <code>nvdir</code> directory with name
+     * <code>patchV.dat</code> where V is the old application version.
+     */
+    public void createDiff (File nvdir, File ovdir, boolean verbose)
+        throws IOException
+    {
+        // sanity check
+        String nvers = nvdir.getName();
+        String overs = ovdir.getName();
+        try {
+            if (Long.parseLong(nvers) <= Long.parseLong(overs)) {
+                String err = "New version (" + nvers + ") must be greater " +
+                    "than old version (" + overs + ").";
+                throw new IOException(err);
+            }
+        } catch (NumberFormatException nfe) {
+            throw new IOException("Non-numeric versions? [nvers=" + nvers +
+                                  ", overs=" + overs + "].");
+        }
+
+        Application oapp = new Application(new EnvConfig(ovdir));
+        oapp.init(false);
+        ArrayList<Resource> orsrcs = new ArrayList<>();
+        orsrcs.addAll(oapp.getCodeResources());
+        orsrcs.addAll(oapp.getResources());
+
+        Application napp = new Application(new EnvConfig(nvdir));
+        napp.init(false);
+        ArrayList<Resource> nrsrcs = new ArrayList<>();
+        nrsrcs.addAll(napp.getCodeResources());
+        nrsrcs.addAll(napp.getResources());
+
+        // first create a patch for the main application
+        File patch = new File(nvdir, "patch" + overs + ".dat");
+        createPatch(patch, orsrcs, nrsrcs, verbose);
+
+        // next create patches for any auxiliary resource groups
+        for (Application.AuxGroup ag : napp.getAuxGroups()) {
+            orsrcs = new ArrayList<>();
+            Application.AuxGroup oag = oapp.getAuxGroup(ag.name);
+            if (oag != null) {
+                orsrcs.addAll(oag.codes);
+                orsrcs.addAll(oag.rsrcs);
+            }
+            nrsrcs = new ArrayList<>();
+            nrsrcs.addAll(ag.codes);
+            nrsrcs.addAll(ag.rsrcs);
+            patch = new File(nvdir, "patch-" + ag.name + overs + ".dat");
+            createPatch(patch, orsrcs, nrsrcs, verbose);
+        }
+    }
+
+    protected void createPatch (File patch, ArrayList<Resource> orsrcs,
+                                ArrayList<Resource> nrsrcs, boolean verbose)
+        throws IOException
+    {
+        int version = Digest.VERSION;
+        MessageDigest md = Digest.getMessageDigest(version);
+        try (FileOutputStream fos = new FileOutputStream(patch);
+             BufferedOutputStream buffered = new BufferedOutputStream(fos);
+             JarOutputStream jout = new JarOutputStream(buffered)) {
+
+            // for each file in the new application, it either already exists
+            // in the old application, or it is new
+            for (Resource rsrc : nrsrcs) {
+                int oidx = orsrcs.indexOf(rsrc);
+                Resource orsrc = (oidx == -1) ? null : orsrcs.remove(oidx);
+                if (orsrc != null) {
+                    // first see if they are the same
+                    String odig = orsrc.computeDigest(version, md, null);
+                    String ndig = rsrc.computeDigest(version, md, null);
+                    if (odig.equals(ndig)) {
+                        if (verbose) {
+                            System.out.println("Unchanged: " + rsrc.getPath());
+                        }
+                        // by leaving it out, it will be left as is during the
+                        // patching process
+                        continue;
+                    }
+
+                    // otherwise potentially create a jar diff
+                    if (rsrc.getPath().endsWith(".jar")) {
+                        if (verbose) {
+                            System.out.println("JarDiff: " + rsrc.getPath());
+                        }
+                        // here's a juicy one: JarDiff blindly pulls ZipEntry
+                        // objects out of one jar file and stuffs them into
+                        // another without clearing out things like the
+                        // compressed size, so if, for whatever reason (like
+                        // different JRE versions or phase of the moon) the
+                        // compressed size in the old jar file is different
+                        // than the compressed size generated when creating the
+                        // jardiff jar file, ZipOutputStream will choke and
+                        // we'll be hosed; so we recreate the jar files in
+                        // their entirety before running jardiff on 'em
+                        File otemp = rebuildJar(orsrc.getLocal());
+                        File temp = rebuildJar(rsrc.getLocal());
+                        jout.putNextEntry(new ZipEntry(rsrc.getPath() + Patcher.PATCH));
+                        jarDiff(otemp, temp, jout);
+                        FileUtil.deleteHarder(otemp);
+                        FileUtil.deleteHarder(temp);
+                        continue;
+                    }
+                }
+
+                if (verbose) {
+                    System.out.println("Addition: " + rsrc.getPath());
+                }
+                jout.putNextEntry(new ZipEntry(rsrc.getPath() + Patcher.CREATE));
+                pipe(rsrc.getLocal(), jout);
+            }
+
+            // now any file remaining in orsrcs needs to be removed
+            for (Resource rsrc : orsrcs) {
+                // add an entry with the resource name and the deletion suffix
+                if (verbose) {
+                    System.out.println("Removal: " + rsrc.getPath());
+                }
+                jout.putNextEntry(new ZipEntry(rsrc.getPath() + Patcher.DELETE));
+            }
+
+            System.out.println("Created patch file: " + patch);
+
+        } catch (IOException ioe) {
+            FileUtil.deleteHarder(patch);
+            throw ioe;
+        }
+    }
+
+    protected File rebuildJar (File target)
+        throws IOException
+    {
+        File temp = File.createTempFile("differ", "jar");
+        try (JarFile jar = new JarFile(target);
+             FileOutputStream tempFos = new FileOutputStream(temp);
+             BufferedOutputStream tempBos = new BufferedOutputStream(tempFos);
+             JarOutputStream jout = new JarOutputStream(tempBos)) {
+            byte[] buffer = new byte[4096];
+            for (Enumeration< JarEntry > iter = jar.entries(); iter.hasMoreElements();) {
+                JarEntry entry = iter.nextElement();
+                entry.setCompressedSize(-1);
+                jout.putNextEntry(entry);
+                try (InputStream in = jar.getInputStream(entry)) {
+                    int size = in.read(buffer);
+                    while (size != -1) {
+                        jout.write(buffer, 0, size);
+                        size = in.read(buffer);
+                    }
+                }
+            }
+        }
+        return temp;
+    }
+
+    protected void jarDiff (File ofile, File nfile, JarOutputStream jout)
+        throws IOException
+    {
+        JarDiff.createPatch(ofile.getPath(), nfile.getPath(), jout, false);
+    }
+
+    public static void main (String[] args)
+    {
+        if (args.length < 2) {
+            System.err.println(
+                "Usage: Differ [-verbose] new_vers_dir old_vers_dir");
+            System.exit(255);
+        }
+        Differ differ = new Differ();
+        boolean verbose = false;
+        int aidx = 0;
+        if (args[0].equals("-verbose")) {
+            verbose = true;
+            aidx++;
+        }
+        try {
+            differ.createDiff(new File(args[aidx++]),
+                              new File(args[aidx++]), verbose);
+        } catch (IOException ioe) {
+            System.err.println("Error: " + ioe.getMessage());
+            System.exit(255);
+        }
+    }
+
+    protected static void pipe (File file, JarOutputStream jout)
+        throws IOException
+    {
+        try (FileInputStream fin = new FileInputStream(file)) {
+            StreamUtil.copy(fin, jout);
+        }
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java
new file mode 100644 (file)
index 0000000..b04a653
--- /dev/null
@@ -0,0 +1,129 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.tools;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.Signature;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.threerings.getdown.data.Application;
+import com.threerings.getdown.data.Digest;
+import com.threerings.getdown.data.EnvConfig;
+import com.threerings.getdown.data.Resource;
+import com.threerings.getdown.util.Base64;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Handles the generation of the digest.txt file.
+ */
+public class Digester
+{
+    /**
+     * A command line entry point for the digester.
+     */
+    public static void main (String[] args)
+        throws IOException, GeneralSecurityException
+    {
+        switch (args.length) {
+        case 1:
+            createDigests(new File(args[0]), null, null, null);
+            break;
+        case 4:
+            createDigests(new File(args[0]), new File(args[1]), args[2], args[3]);
+            break;
+        default:
+            System.err.println("Usage: Digester app_dir [keystore_path password alias]");
+            System.exit(255);
+        }
+    }
+
+    /**
+     * Creates digest file(s) and optionally signs them if {@code keystore} is not null.
+     */
+    public static void createDigests (File appdir, File keystore, String password, String alias)
+        throws IOException, GeneralSecurityException
+    {
+        for (int version = 1; version <= Digest.VERSION; version++) {
+            createDigest(version, appdir);
+            if (keystore != null) {
+                signDigest(version, appdir, keystore, password, alias);
+            }
+        }
+    }
+
+    /**
+     * Creates a digest file in the specified application directory.
+     */
+    public static void createDigest (int version, File appdir)
+        throws IOException
+    {
+        File target = new File(appdir, Digest.digestFile(version));
+        System.out.println("Generating digest file '" + target + "'...");
+
+        // create our application and instruct it to parse its business
+        Application app = new Application(new EnvConfig(appdir));
+        app.init(false);
+
+        List<Resource> rsrcs = new ArrayList<>();
+        rsrcs.add(app.getConfigResource());
+        rsrcs.addAll(app.getCodeResources());
+        rsrcs.addAll(app.getResources());
+        for (Application.AuxGroup ag : app.getAuxGroups()) {
+            rsrcs.addAll(ag.codes);
+            rsrcs.addAll(ag.rsrcs);
+        }
+
+        // now generate the digest file
+        Digest.createDigest(version, rsrcs, target);
+    }
+
+    /**
+     * Creates a digest file in the specified application directory.
+     */
+    public static void signDigest (int version, File appdir,
+                                   File storePath, String storePass, String storeAlias)
+        throws IOException, GeneralSecurityException
+    {
+        String filename = Digest.digestFile(version);
+        File inputFile = new File(appdir, filename);
+        File signatureFile = new File(appdir, filename + Application.SIGNATURE_SUFFIX);
+
+        try (FileInputStream storeInput = new FileInputStream(storePath);
+             FileInputStream dataInput = new FileInputStream(inputFile);
+             FileOutputStream signatureOutput = new FileOutputStream(signatureFile)) {
+
+            // initialize the keystore
+            KeyStore store = KeyStore.getInstance("JKS");
+            store.load(storeInput, storePass.toCharArray());
+            PrivateKey key = (PrivateKey)store.getKey(storeAlias, storePass.toCharArray());
+
+            // sign the digest file
+            String algo = Digest.sigAlgorithm(version);
+            Signature sig = Signature.getInstance(algo);
+            byte[] buffer = new byte[8192];
+            int length;
+
+            sig.initSign(key);
+            while ((length = dataInput.read(buffer)) != -1) {
+                sig.update(buffer, 0, length);
+            }
+
+            // Write out the signature
+            String signed = Base64.encodeToString(sig.sign(), Base64.DEFAULT);
+            signatureOutput.write(signed.getBytes(UTF_8));
+        }
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiff.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiff.java
new file mode 100644 (file)
index 0000000..1cea0ea
--- /dev/null
@@ -0,0 +1,449 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+/*
+ * @(#)JarDiff.java 1.7 05/11/17
+ *
+ * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * -Redistribution of source code must retain the above copyright notice, this
+ *  list of conditions and the following disclaimer.
+ *
+ * -Redistribution in binary form must reproduce the above copyright notice,
+ *  this list of conditions and the following disclaimer in the documentation
+ *  and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
+ * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
+ * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
+ * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
+ * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
+ * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
+ * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or intended
+ * for use in the design, construction, operation or maintenance of any
+ * nuclear facility.
+ */
+
+package com.threerings.getdown.tools;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * JarDiff is able to create a jar file containing the delta between two jar files (old and new).
+ * The delta jar file can then be applied to the old jar file to reconstruct the new jar file.
+ *
+ * <p> Refer to the JNLP spec for details on how this is done.
+ *
+ * @version 1.13, 06/26/03
+ */
+public class JarDiff implements JarDiffCodes
+{
+    private static final int DEFAULT_READ_SIZE = 2048;
+    private static byte[] newBytes = new byte[DEFAULT_READ_SIZE];
+    private static byte[] oldBytes = new byte[DEFAULT_READ_SIZE];
+
+    // The JARDiff.java is the stand-alone jardiff.jar tool. Thus, we do not depend on Globals.java
+    // and other stuff here. Instead, we use an explicit _debug flag.
+    private static boolean _debug;
+
+    /**
+     * Creates a patch from the two passed in files, writing the result to <code>os</code>.
+     */
+    public static void createPatch (String oldPath, String newPath,
+                                    OutputStream os, boolean minimal) throws IOException
+    {
+        try (JarFile2 oldJar = new JarFile2(oldPath);
+             JarFile2 newJar = new JarFile2(newPath)) {
+
+            HashMap<String,String> moved = new HashMap<>();
+            HashSet<String> implicit = new HashSet<>();
+            HashSet<String> moveSrc = new HashSet<>();
+            HashSet<String> newEntries = new HashSet<>();
+
+            // FIRST PASS
+            // Go through the entries in new jar and
+            // determine which files are candidates for implicit moves
+            // ( files that has the same filename and same content in old.jar
+            // and new.jar )
+            // and for files that cannot be implicitly moved, we will either
+            // find out whether it is moved or new (modified)
+            for (JarEntry newEntry : newJar) {
+                String newname = newEntry.getName();
+
+                // Return best match of contents, will return a name match if possible
+                String oldname = oldJar.getBestMatch(newJar, newEntry);
+                if (oldname == null) {
+                    // New or modified entry
+                    if (_debug) {
+                        System.out.println("NEW: "+ newname);
+                    }
+                    newEntries.add(newname);
+                } else {
+                    // Content already exist - need to do a move
+
+                    // Should do implicit move? Yes, if names are the same, and
+                    // no move command already exist from oldJar
+                    if (oldname.equals(newname) && !moveSrc.contains(oldname)) {
+                        if (_debug) {
+                            System.out.println(newname + " added to implicit set!");
+                        }
+                        implicit.add(newname);
+                    } else {
+                        // The 1.0.1/1.0 JarDiffPatcher cannot handle
+                        // multiple MOVE command with same src.
+                        // The work around here is if we are going to generate
+                        // a MOVE command with duplicate src, we will
+                        // instead add the target as a new file.  This way
+                        // the jardiff can be applied by 1.0.1/1.0
+                        // JarDiffPatcher also.
+                        if (!minimal && (implicit.contains(oldname) ||
+                                         moveSrc.contains(oldname) )) {
+
+                            // generate non-minimal jardiff
+                            // for backward compatibility
+
+                            if (_debug) {
+
+                                System.out.println("NEW: "+ newname);
+                            }
+                            newEntries.add(newname);
+                        } else {
+                            // Use newname as key, since they are unique
+                            if (_debug) {
+                                System.err.println("moved.put " + newname + " " + oldname);
+                            }
+                            moved.put(newname, oldname);
+                            moveSrc.add(oldname);
+                        }
+                        // Check if this disables an implicit 'move <oldname> <oldname>'
+                        if (implicit.contains(oldname) && minimal) {
+
+                            if (_debug) {
+                                System.err.println("implicit.remove " + oldname);
+
+                                System.err.println("moved.put " + oldname + " " + oldname);
+
+                            }
+                            implicit.remove(oldname);
+                            moved.put(oldname, oldname);
+                            moveSrc.add(oldname);
+                        }
+                    }
+                }
+            }
+
+            // SECOND PASS: <deleted files> = <oldjarnames> - <implicitmoves> -
+            // <source of move commands> - <new or modified entries>
+            ArrayList<String> deleted = new ArrayList<>();
+            for (JarEntry oldEntry : oldJar) {
+                String oldName = oldEntry.getName();
+                if (!implicit.contains(oldName) && !moveSrc.contains(oldName)
+                    && !newEntries.contains(oldName)) {
+                    if (_debug) {
+                        System.err.println("deleted.add " + oldName);
+                    }
+                    deleted.add(oldName);
+                }
+            }
+
+            //DEBUG
+            if (_debug) {
+                //DEBUG:  print out moved map
+                System.out.println("MOVED MAP!!!");
+                for (Map.Entry<String,String> entry : moved.entrySet()) {
+                    System.out.println(entry);
+                }
+
+                //DEBUG:  print out IMOVE map
+                System.out.println("IMOVE MAP!!!");
+                for (String newName : implicit) {
+                    System.out.println("key is " + newName);
+                }
+            }
+
+            JarOutputStream jos = new JarOutputStream(os);
+
+            // Write out all the MOVEs and REMOVEs
+            createIndex(jos, deleted, moved);
+
+            // Put in New and Modified entries
+            for (String newName : newEntries) {
+                if (_debug) {
+                    System.out.println("New File: " + newName);
+                }
+                writeEntry(jos, newJar.getEntryByName(newName), newJar);
+            }
+
+            jos.finish();
+//            jos.close();
+        }
+    }
+
+    /**
+     * Writes the index file out to <code>jos</code>.
+     * <code>oldEntries</code> gives the names of the files that were removed,
+     * <code>movedMap</code> maps from the new name to the old name.
+     */
+    private static void createIndex (JarOutputStream jos, List<String> oldEntries,
+                                     Map<String,String> movedMap)
+        throws IOException
+    {
+        StringWriter writer = new StringWriter();
+        writer.write(VERSION_HEADER);
+        writer.write("\r\n");
+
+        // Write out entries that have been removed
+        for (String name : oldEntries) {
+            writer.write(REMOVE_COMMAND);
+            writer.write(" ");
+            writeEscapedString(writer, name);
+            writer.write("\r\n");
+        }
+
+        // And those that have moved
+        for (String newName : movedMap.keySet()) {
+            String oldName = movedMap.get(newName);
+            writer.write(MOVE_COMMAND);
+            writer.write(" ");
+            writeEscapedString(writer, oldName);
+            writer.write(" ");
+            writeEscapedString(writer, newName);
+            writer.write("\r\n");
+        }
+
+        jos.putNextEntry(new JarEntry(INDEX_NAME));
+        byte[] bytes = writer.toString().getBytes(UTF_8);
+        jos.write(bytes, 0, bytes.length);
+    }
+
+    protected static Writer writeEscapedString (Writer writer, String string)
+        throws IOException
+    {
+        int index = 0;
+        int last = 0;
+        char[] chars = null;
+
+        while ((index = string.indexOf(' ', index)) != -1) {
+            if (last != index) {
+                if (chars == null) {
+                    chars = string.toCharArray();
+                }
+                writer.write(chars, last, index - last);
+            }
+            last = index;
+            index++;
+            writer.write('\\');
+        }
+        if (last != 0 && chars != null) {
+            writer.write(chars, last, chars.length - last);
+        }
+        else {
+            // no spaces
+            writer.write(string);
+        }
+
+        return writer;
+    }
+
+    private static void writeEntry (JarOutputStream jos, JarEntry entry, JarFile2 file)
+        throws IOException
+    {
+        try (InputStream data = file.getJarFile().getInputStream(entry)) {
+            jos.putNextEntry(entry);
+            int size = data.read(newBytes);
+            while (size != -1) {
+                jos.write(newBytes, 0, size);
+                size = data.read(newBytes);
+            }
+        }
+    }
+
+    /**
+     * JarFile2 wraps a JarFile providing some convenience methods.
+     */
+    private static class JarFile2 implements Iterable<JarEntry>, Closeable
+    {
+        private JarFile _jar;
+        private List<JarEntry> _entries;
+        private HashMap<String,JarEntry> _nameToEntryMap;
+        private HashMap<Long,LinkedList<JarEntry>> _crcToEntryMap;
+
+        public JarFile2 (String path) throws IOException {
+            _jar = new JarFile(new File(path));
+            index();
+        }
+
+        public JarFile getJarFile () {
+            return _jar;
+        }
+
+        // from interface Iterable<JarEntry>
+        @Override
+        public Iterator<JarEntry> iterator () {
+            return _entries.iterator();
+        }
+
+        public JarEntry getEntryByName (String name) {
+            return _nameToEntryMap.get(name);
+        }
+
+        /**
+         * Returns true if the two InputStreams differ.
+         */
+        private static boolean differs (InputStream oldIS, InputStream newIS) throws IOException {
+            int newSize = 0;
+            int oldSize;
+            int total = 0;
+            boolean retVal = false;
+
+            while (newSize != -1) {
+                newSize = newIS.read(newBytes);
+                oldSize = oldIS.read(oldBytes);
+
+                if (newSize != oldSize) {
+                    if (_debug) {
+                        System.out.println("\tread sizes differ: " + newSize +
+                                           " " + oldSize + " total " + total);
+                    }
+                    retVal = true;
+                    break;
+                }
+                if (newSize > 0) {
+                    while (--newSize >= 0) {
+                        total++;
+                        if (newBytes[newSize] != oldBytes[newSize]) {
+                            if (_debug) {
+                                System.out.println("\tbytes differ at " +
+                                                   total);
+                            }
+                            retVal = true;
+                            break;
+                        }
+                        if ( retVal ) {
+                            //Jump out
+                            break;
+                        }
+                        newSize = 0;
+                    }
+                }
+            }
+
+            return retVal;
+        }
+
+        public String getBestMatch (JarFile2 file, JarEntry entry) throws IOException {
+            // check for same name and same content, return name if found
+            if (contains(file, entry)) {
+                return (entry.getName());
+            }
+
+            // return name of same content file or null
+            return (hasSameContent(file,entry));
+        }
+
+        public boolean contains (JarFile2 f, JarEntry e) throws IOException {
+
+            JarEntry thisEntry = getEntryByName(e.getName());
+
+            // Look up name in 'this' Jar2File - if not exist return false
+            if (thisEntry == null)
+                return false;
+
+            // Check CRC - if no match - return false
+            if (thisEntry.getCrc() != e.getCrc())
+                return false;
+
+            // Check contents - if no match - return false
+            try (InputStream oldIS = getJarFile().getInputStream(thisEntry);
+                 InputStream newIS = f.getJarFile().getInputStream(e)) {
+                return !differs(oldIS, newIS);
+            }
+        }
+
+        public String hasSameContent (JarFile2 file, JarEntry entry) throws IOException {
+            String thisName = null;
+            Long crcL = Long.valueOf(entry.getCrc());
+            // check if this jar contains files with the passed in entry's crc
+            if (_crcToEntryMap.containsKey(crcL)) {
+                // get the Linked List with files with the crc
+                LinkedList<JarEntry> ll = _crcToEntryMap.get(crcL);
+                // go through the list and check for content match
+                ListIterator<JarEntry> li = ll.listIterator(0);
+                while (li.hasNext()) {
+                    JarEntry thisEntry = li.next();
+                    // check for content match
+                    try (InputStream oldIS = getJarFile().getInputStream(thisEntry);
+                         InputStream newIS = file.getJarFile().getInputStream(entry)) {
+                        if (!differs(oldIS, newIS)) {
+                            thisName = thisEntry.getName();
+                            return thisName;
+                        }
+                    }
+                }
+            }
+            return thisName;
+        }
+
+        private void index () throws IOException {
+            Enumeration<JarEntry> entries = _jar.entries();
+
+            _nameToEntryMap = new HashMap<>();
+            _crcToEntryMap = new HashMap<>();
+            _entries = new ArrayList<>();
+            if (_debug) {
+                System.out.println("indexing: " + _jar.getName());
+            }
+            if (entries != null) {
+                while (entries.hasMoreElements()) {
+                    JarEntry entry = entries.nextElement();
+                    long crc = entry.getCrc();
+                    Long crcL = Long.valueOf(crc);
+                    if (_debug) {
+                        System.out.println("\t" + entry.getName() + " CRC " + crc);
+                    }
+
+                    _nameToEntryMap.put(entry.getName(), entry);
+                    _entries.add(entry);
+
+                    // generate the CRC to entries map
+                    if (_crcToEntryMap.containsKey(crcL)) {
+                        // key exist, add the entry to the correcponding linked list
+                        LinkedList<JarEntry> ll = _crcToEntryMap.get(crcL);
+                        ll.add(entry);
+                        _crcToEntryMap.put(crcL, ll);
+
+                    } else {
+                        // create a new entry in the hashmap for the new key
+                        LinkedList<JarEntry> ll = new LinkedList<JarEntry>();
+                        ll.add(entry);
+                        _crcToEntryMap.put(crcL, ll);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void close() throws IOException {
+            _jar.close();
+        }
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffCodes.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffCodes.java
new file mode 100644 (file)
index 0000000..3b5db80
--- /dev/null
@@ -0,0 +1,24 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.tools;
+
+/**
+ * Constants shared by {@link JarDiff} and {@link JarDiffPatcher}.
+ */
+public interface JarDiffCodes
+{
+    /** The name of the jardiff control file. */
+    String INDEX_NAME = "META-INF/INDEX.JD";
+
+    /** The version header used in the control file. */
+    String VERSION_HEADER = "version 1.0";
+
+    /** A jardiff command to remove an entry. */
+    String REMOVE_COMMAND = "remove";
+
+    /** A jardiff command to move an entry. */
+    String MOVE_COMMAND = "move";
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffPatcher.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffPatcher.java
new file mode 100644 (file)
index 0000000..b5a0a17
--- /dev/null
@@ -0,0 +1,292 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.tools;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+
+import com.threerings.getdown.util.ProgressObserver;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Applies a jardiff patch to a jar file.
+ */
+public class JarDiffPatcher implements JarDiffCodes
+{
+    /**
+     * Patches the specified jar file using the supplied patch file and writing
+     * the new jar file to the supplied target.
+     *
+     * @param jarPath the path to the original jar file.
+     * @param diffPath the path to the jardiff patch file.
+     * @param target the output stream to which we will write the patched jar.
+     * @param observer an optional observer to be notified of patching progress.
+     *
+     * @throws IOException if any problem occurs during patching.
+     */
+    public void patchJar (String jarPath, String diffPath, File target, ProgressObserver observer)
+        throws IOException
+    {
+        File oldFile = new File(jarPath), diffFile = new File(diffPath);
+
+        try (JarFile oldJar = new JarFile(oldFile);
+             JarFile jarDiff = new JarFile(diffFile);
+             JarOutputStream jos = new JarOutputStream(new FileOutputStream(target))) {
+
+            Set<String> ignoreSet = new HashSet<>();
+            Map<String, String> renameMap = new HashMap<>();
+            determineNameMapping(jarDiff, ignoreSet, renameMap);
+
+            // get all keys in renameMap
+            String[] keys = renameMap.keySet().toArray(new String[renameMap.size()]);
+
+            // Files to implicit move
+            Set<String> oldjarNames  = new HashSet<>();
+            Enumeration<JarEntry> oldEntries = oldJar.entries();
+            if (oldEntries != null) {
+                while  (oldEntries.hasMoreElements()) {
+                    oldjarNames.add(oldEntries.nextElement().getName());
+                }
+            }
+
+            // size depends on the three parameters below, which is basically the
+            // counter for each loop that do the actual writes to the output file
+            // since oldjarNames.size() changes in the first two loop below, we
+            // need to adjust the size accordingly also when oldjarNames.size()
+            // changes
+            double size = oldjarNames.size() + keys.length + jarDiff.size();
+            double currentEntry = 0;
+
+            // Handle all remove commands
+            oldjarNames.removeAll(ignoreSet);
+            size -= ignoreSet.size();
+
+            // Add content from JARDiff
+            Enumeration<JarEntry> entries = jarDiff.entries();
+            if (entries != null) {
+                while (entries.hasMoreElements()) {
+                    JarEntry entry = entries.nextElement();
+                    if (!INDEX_NAME.equals(entry.getName())) {
+                        updateObserver(observer, currentEntry, size);
+                        currentEntry++;
+                        writeEntry(jos, entry, jarDiff);
+
+                        // Remove entry from oldjarNames since no implicit move is
+                        // needed
+                        boolean wasInOld = oldjarNames.remove(entry.getName());
+
+                        // Update progress counters. If it was in old, we do not
+                        // need an implicit move, so adjust total size.
+                        if (wasInOld) {
+                            size--;
+                        }
+
+                    } else {
+                        // no write is done, decrement size
+                        size--;
+                    }
+                }
+            }
+
+            // go through the renameMap and apply move for each entry
+            for (String newName : keys) {
+                // Apply move <oldName> <newName> command
+                String oldName = renameMap.get(newName);
+
+                // Get source JarEntry
+                JarEntry oldEntry = oldJar.getJarEntry(oldName);
+                if (oldEntry == null) {
+                    String moveCmd = MOVE_COMMAND + oldName + " " + newName;
+                    throw new IOException("error.badmove: " + moveCmd);
+                }
+
+                // Create dest JarEntry
+                JarEntry newEntry = new JarEntry(newName);
+                newEntry.setTime(oldEntry.getTime());
+                newEntry.setSize(oldEntry.getSize());
+                newEntry.setCompressedSize(oldEntry.getCompressedSize());
+                newEntry.setCrc(oldEntry.getCrc());
+                newEntry.setMethod(oldEntry.getMethod());
+                newEntry.setExtra(oldEntry.getExtra());
+                newEntry.setComment(oldEntry.getComment());
+
+                updateObserver(observer, currentEntry, size);
+                currentEntry++;
+
+                try (InputStream data = oldJar.getInputStream(oldEntry)) {
+                    writeEntry(jos, newEntry, data);
+                }
+
+                // Remove entry from oldjarNames since no implicit move is needed
+                boolean wasInOld = oldjarNames.remove(oldName);
+
+                // Update progress counters. If it was in old, we do not need an
+                // implicit move, so adjust total size.
+                if (wasInOld) {
+                    size--;
+                }
+            }
+
+            // implicit move
+            Iterator<String> iEntries = oldjarNames.iterator();
+            if (iEntries != null) {
+                while (iEntries.hasNext()) {
+                    String name = iEntries.next();
+                    JarEntry entry = oldJar.getJarEntry(name);
+                    if (entry == null) {
+                        // names originally retrieved from the JAR, so this should never happen
+                        throw new AssertionError("JAR entry not found: " + name);
+                    }
+                    updateObserver(observer, currentEntry, size);
+                    currentEntry++;
+                    writeEntry(jos, entry, oldJar);
+                }
+            }
+            updateObserver(observer, currentEntry, size);
+        }
+    }
+
+    protected void updateObserver (ProgressObserver observer, double currentSize, double size)
+    {
+        if (observer != null) {
+            observer.progress((int)(100*currentSize/size));
+        }
+    }
+
+    protected void determineNameMapping (
+        JarFile jarDiff, Set<String> ignoreSet, Map<String, String> renameMap)
+        throws IOException
+    {
+        InputStream is = jarDiff.getInputStream(jarDiff.getEntry(INDEX_NAME));
+        if (is == null) {
+            throw new IOException("error.noindex");
+        }
+
+        LineNumberReader indexReader =
+            new LineNumberReader(new InputStreamReader(is, UTF_8));
+        String line = indexReader.readLine();
+        if (line == null || !line.equals(VERSION_HEADER)) {
+            throw new IOException("jardiff.error.badheader: " + line);
+        }
+
+        while ((line = indexReader.readLine()) != null) {
+            if (line.startsWith(REMOVE_COMMAND)) {
+                List<String> sub = getSubpaths(
+                    line.substring(REMOVE_COMMAND.length()));
+
+                if (sub.size() != 1) {
+                    throw new IOException("error.badremove: " + line);
+                }
+                ignoreSet.add(sub.get(0));
+
+            } else if (line.startsWith(MOVE_COMMAND)) {
+                List<String> sub = getSubpaths(
+                    line.substring(MOVE_COMMAND.length()));
+                if (sub.size() != 2) {
+                    throw new IOException("error.badmove: " + line);
+                }
+
+                // target of move should be the key
+                if (renameMap.put(sub.get(1), sub.get(0)) != null) {
+                    // invalid move - should not move to same target twice
+                    throw new IOException("error.badmove: " + line);
+                }
+
+            } else if (line.length() > 0) {
+                throw new IOException("error.badcommand: " + line);
+            }
+        }
+    }
+
+    protected List<String> getSubpaths (String path)
+    {
+        int index = 0;
+        int length = path.length();
+        ArrayList<String> sub = new ArrayList<>();
+
+        while (index < length) {
+            while (index < length && Character.isWhitespace
+                   (path.charAt(index))) {
+                index++;
+            }
+            if (index < length) {
+                int start = index;
+                int last = start;
+                String subString = null;
+
+                while (index < length) {
+                    char aChar = path.charAt(index);
+                    if (aChar == '\\' && (index + 1) < length &&
+                        path.charAt(index + 1) == ' ') {
+
+                        if (subString == null) {
+                            subString = path.substring(last, index);
+                        } else {
+                            subString += path.substring(last, index);
+                        }
+                        last = ++index;
+                    } else if (Character.isWhitespace(aChar)) {
+                        break;
+                    }
+                    index++;
+                }
+                if (last != index) {
+                    if (subString == null) {
+                        subString = path.substring(last, index);
+                    } else {
+                        subString += path.substring(last, index);
+                    }
+                }
+                sub.add(subString);
+            }
+        }
+        return sub;
+    }
+
+    protected void writeEntry (JarOutputStream jos, JarEntry entry, JarFile file)
+        throws IOException
+    {
+        try (InputStream data = file.getInputStream(entry)) {
+            writeEntry(jos, entry, data);
+        }
+    }
+
+    protected void writeEntry (JarOutputStream jos, JarEntry entry, InputStream data)
+        throws IOException
+    {
+        jos.putNextEntry(new JarEntry(entry.getName()));
+
+        // Read the entry
+        int size = data.read(newBytes);
+        while (size != -1) {
+            jos.write(newBytes, 0, size);
+            size = data.read(newBytes);
+        }
+    }
+
+    protected static final int DEFAULT_READ_SIZE = 2048;
+
+    protected static byte[] newBytes = new byte[DEFAULT_READ_SIZE];
+    protected static byte[] oldBytes = new byte[DEFAULT_READ_SIZE];
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Patcher.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Patcher.java
new file mode 100644 (file)
index 0000000..4ead59b
--- /dev/null
@@ -0,0 +1,205 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.tools;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import com.threerings.getdown.util.FileUtil;
+import com.threerings.getdown.util.ProgressObserver;
+import com.threerings.getdown.util.StreamUtil;
+
+import static com.threerings.getdown.Log.log;
+
+/**
+ * Applies a unified patch file to an application directory, providing
+ * percentage completion feedback along the way. <em>Note:</em> the
+ * patcher is not thread safe. Create a separate patcher instance for each
+ * patching action that is desired.
+ */
+public class Patcher
+{
+    /** A suffix appended to file names to indicate that a file should be newly created. */
+    public static final String CREATE = ".create";
+
+    /** A suffix appended to file names to indicate that a file should be patched. */
+    public static final String PATCH = ".patch";
+
+    /** A suffix appended to file names to indicate that a file should be deleted. */
+    public static final String DELETE = ".delete";
+
+    /**
+     * Applies the specified patch file to the application living in the
+     * specified application directory. The supplied observer, if
+     * non-null, will be notified of progress along the way.
+     *
+     * <p><em>Note:</em> this method runs on the calling thread, thus the
+     * caller may want to make use of a separate thread in conjunction
+     * with the patcher so that the user interface is not blocked for the
+     * duration of the patch.
+     */
+    public void patch (File appdir, File patch, ProgressObserver obs)
+        throws IOException
+    {
+        // save this information for later
+        _obs = obs;
+        _plength = patch.length();
+
+        try (JarFile file = new JarFile(patch)) {
+            Enumeration<JarEntry> entries = file.entries(); // old skool!
+            while (entries.hasMoreElements()) {
+                JarEntry entry = entries.nextElement();
+                String path = entry.getName();
+                long elength = entry.getCompressedSize();
+
+                // depending on the suffix, we do The Right Thing (tm)
+                if (path.endsWith(CREATE)) {
+                    path = strip(path, CREATE);
+                    log.info("Creating " + path + "...");
+                    createFile(file, entry, new File(appdir, path));
+
+                } else if (path.endsWith(PATCH)) {
+                    path = strip(path, PATCH);
+                    log.info("Patching " + path + "...");
+                    patchFile(file, entry, appdir, path);
+
+                } else if (path.endsWith(DELETE)) {
+                    path = strip(path, DELETE);
+                    log.info("Removing " + path + "...");
+                    File target = new File(appdir, path);
+                    if (!FileUtil.deleteHarder(target)) {
+                        log.warning("Failure deleting '" + target + "'.");
+                    }
+
+                } else {
+                    log.warning("Skipping bogus patch file entry: " + path);
+                }
+
+                // note that we've completed this entry
+                _complete += elength;
+            }
+        }
+    }
+
+    protected String strip (String path, String suffix)
+    {
+        return path.substring(0, path.length() - suffix.length());
+    }
+
+    protected void createFile (JarFile file, ZipEntry entry, File target)
+    {
+        // create our copy buffer if necessary
+        if (_buffer == null) {
+            _buffer = new byte[COPY_BUFFER_SIZE];
+        }
+
+        // make sure the file's parent directory exists
+        File pdir = target.getParentFile();
+        if (!pdir.exists() && !pdir.mkdirs()) {
+            log.warning("Failed to create parent for '" + target + "'.");
+        }
+
+        try (InputStream in = file.getInputStream(entry);
+             FileOutputStream fout = new FileOutputStream(target)) {
+
+            int total = 0, read;
+            while ((read = in.read(_buffer)) != -1) {
+                total += read;
+                fout.write(_buffer, 0, read);
+                updateProgress(total);
+            }
+
+        } catch (IOException ioe) {
+            log.warning("Error creating '" + target + "': " + ioe);
+        }
+    }
+
+    protected void patchFile (JarFile file, ZipEntry entry,
+                              File appdir, String path)
+    {
+        File target = new File(appdir, path);
+        File patch = new File(appdir, entry.getName());
+        File otarget = new File(appdir, path + ".old");
+        JarDiffPatcher patcher = null;
+
+        // make sure no stale old target is lying around to mess us up
+        FileUtil.deleteHarder(otarget);
+
+        // pipe the contents of the patch into a file
+        try (InputStream in = file.getInputStream(entry);
+             FileOutputStream fout = new FileOutputStream(patch)) {
+
+            StreamUtil.copy(in, fout);
+            StreamUtil.close(fout);
+
+            // move the current version of the jar to .old
+            if (!FileUtil.renameTo(target, otarget)) {
+                log.warning("Failed to .oldify '" + target + "'.");
+                return;
+            }
+
+            // we'll need this to pass progress along to our observer
+            final long elength = entry.getCompressedSize();
+            ProgressObserver obs = new ProgressObserver() {
+                public void progress (int percent) {
+                    updateProgress((int)(percent * elength / 100));
+                }
+            };
+
+            // now apply the patch to create the new target file
+            patcher = new JarDiffPatcher();
+            patcher.patchJar(otarget.getPath(), patch.getPath(), target, obs);
+
+        } catch (IOException ioe) {
+            if (patcher == null) {
+                log.warning("Failed to write patch file '" + patch + "': " + ioe);
+            } else {
+                log.warning("Error patching '" + target + "': " + ioe);
+            }
+
+        } finally {
+            // clean up our temporary files
+            FileUtil.deleteHarder(patch);
+            FileUtil.deleteHarder(otarget);
+        }
+    }
+
+    protected void updateProgress (int progress)
+    {
+        if (_obs != null) {
+            _obs.progress((int)(100 * (_complete + progress) / _plength));
+        }
+    }
+
+    public static void main (String[] args)
+    {
+        if (args.length != 2) {
+            System.err.println("Usage: Patcher appdir patch_file");
+            System.exit(-1);
+        }
+
+        Patcher patcher = new Patcher();
+        try {
+            patcher.patch(new File(args[0]), new File(args[1]), null);
+        } catch (IOException ioe) {
+            System.err.println("Error: " + ioe.getMessage());
+            System.exit(-1);
+        }
+    }
+
+    protected ProgressObserver _obs;
+    protected long _complete, _plength;
+    protected byte[] _buffer;
+
+    protected static final int COPY_BUFFER_SIZE = 4096;
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Base64.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Base64.java
new file mode 100644 (file)
index 0000000..2a5db79
--- /dev/null
@@ -0,0 +1,731 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.threerings.getdown.util;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+/**
+ * Utilities for encoding and decoding the Base64 representation of
+ * binary data.  See RFCs <a
+ * href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a
+ * href="http://www.ietf.org/rfc/rfc3548.txt">3548</a>.
+ */
+public class Base64 {
+    /**
+     * Default values for encoder/decoder flags.
+     */
+    public static final int DEFAULT = 0;
+
+    /**
+     * Encoder flag bit to omit the padding '=' characters at the end
+     * of the output (if any).
+     */
+    public static final int NO_PADDING = 1;
+
+    /**
+     * Encoder flag bit to omit all line terminators (i.e., the output
+     * will be on one long line).
+     */
+    public static final int NO_WRAP = 2;
+
+    /**
+     * Encoder flag bit to indicate lines should be terminated with a
+     * CRLF pair instead of just an LF.  Has no effect if {@code
+     * NO_WRAP} is specified as well.
+     */
+    public static final int CRLF = 4;
+
+    /**
+     * Encoder/decoder flag bit to indicate using the "URL and
+     * filename safe" variant of Base64 (see RFC 3548 section 4) where
+     * {@code -} and {@code _} are used in place of {@code +} and
+     * {@code /}.
+     */
+    public static final int URL_SAFE = 8;
+
+    /**
+     * Flag to pass to {@code Base64OutputStream} to indicate that it
+     * should not close the output stream it is wrapping when it
+     * itself is closed.
+     */
+    public static final int NO_CLOSE = 16;
+
+    //  --------------------------------------------------------
+    //  shared code
+    //  --------------------------------------------------------
+
+    /* package */ static abstract class Coder {
+        public byte[] output;
+        public int op;
+
+        /**
+         * Encode/decode another block of input data.  this.output is
+         * provided by the caller, and must be big enough to hold all
+         * the coded data.  On exit, this.opwill be set to the length
+         * of the coded data.
+         *
+         * @param finish true if this is the final call to process for
+         *        this object.  Will finalize the coder state and
+         *        include any final bytes in the output.
+         *
+         * @return true if the input so far is good; false if some
+         *         error has been detected in the input stream..
+         */
+        public abstract boolean process(byte[] input, int offset, int len, boolean finish);
+
+        /**
+         * @return the maximum number of bytes a call to process()
+         * could produce for the given number of input bytes.  This may
+         * be an overestimate.
+         */
+        public abstract int maxOutputSize(int len);
+    }
+
+    //  --------------------------------------------------------
+    //  decoding
+    //  --------------------------------------------------------
+
+    /**
+     * Decode the Base64-encoded data in input and return the data in
+     * a new byte array.
+     *
+     * <p>The padding '=' characters at the end are considered optional, but
+     * if any are present, there must be the correct number of them.
+     *
+     * @param str    the input String to decode, which is converted to
+     *               bytes using ASCII
+     * @param flags  controls certain features of the decoded output.
+     *               Pass {@code DEFAULT} to decode standard Base64.
+     *
+     * @throws IllegalArgumentException if the input contains
+     * incorrect padding
+     */
+    public static byte[] decode(String str, int flags) {
+        return decode(str.getBytes(US_ASCII), flags);
+    }
+
+    /**
+     * Decode the Base64-encoded data in input and return the data in
+     * a new byte array.
+     *
+     * <p>The padding '=' characters at the end are considered optional, but
+     * if any are present, there must be the correct number of them.
+     *
+     * @param input the input array to decode
+     * @param flags  controls certain features of the decoded output.
+     *               Pass {@code DEFAULT} to decode standard Base64.
+     *
+     * @throws IllegalArgumentException if the input contains
+     * incorrect padding
+     */
+    public static byte[] decode(byte[] input, int flags) {
+        return decode(input, 0, input.length, flags);
+    }
+
+    /**
+     * Decode the Base64-encoded data in input and return the data in
+     * a new byte array.
+     *
+     * <p>The padding '=' characters at the end are considered optional, but
+     * if any are present, there must be the correct number of them.
+     *
+     * @param input  the data to decode
+     * @param offset the position within the input array at which to start
+     * @param len    the number of bytes of input to decode
+     * @param flags  controls certain features of the decoded output.
+     *               Pass {@code DEFAULT} to decode standard Base64.
+     *
+     * @throws IllegalArgumentException if the input contains
+     * incorrect padding
+     */
+    public static byte[] decode(byte[] input, int offset, int len, int flags) {
+        // Allocate space for the most data the input could represent.
+        // (It could contain less if it contains whitespace, etc.)
+        Decoder decoder = new Decoder(flags, new byte[len*3/4]);
+
+        if (!decoder.process(input, offset, len, true)) {
+            throw new IllegalArgumentException("bad base-64");
+        }
+
+        // Maybe we got lucky and allocated exactly enough output space.
+        if (decoder.op == decoder.output.length) {
+            return decoder.output;
+        }
+
+        // Need to shorten the array, so allocate a new one of the
+        // right size and copy.
+        byte[] temp = new byte[decoder.op];
+        System.arraycopy(decoder.output, 0, temp, 0, decoder.op);
+        return temp;
+    }
+
+    /* package */ static class Decoder extends Coder {
+        /**
+         * Lookup table for turning bytes into their position in the
+         * Base64 alphabet.
+         */
+        private static final int DECODE[] = {
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+            -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+            -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        };
+
+        /**
+         * Decode lookup table for the "web safe" variant (RFC 3548
+         * sec. 4) where - and _ replace + and /.
+         */
+        private static final int DECODE_WEBSAFE[] = {
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
+            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
+            -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
+            -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        };
+
+        /** Non-data values in the DECODE arrays. */
+        private static final int SKIP = -1;
+        private static final int EQUALS = -2;
+
+        /**
+         * States 0-3 are reading through the next input tuple.
+         * State 4 is having read one '=' and expecting exactly
+         * one more.
+         * State 5 is expecting no more data or padding characters
+         * in the input.
+         * State 6 is the error state; an error has been detected
+         * in the input and no future input can "fix" it.
+         */
+        private int state;   // state number (0 to 6)
+        private int value;
+
+        final private int[] alphabet;
+
+        public Decoder(int flags, byte[] output) {
+            this.output = output;
+
+            alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE;
+            state = 0;
+            value = 0;
+        }
+
+        /**
+         * @return an overestimate for the number of bytes {@code
+         * len} bytes could decode to.
+         */
+        public int maxOutputSize(int len) {
+            return len * 3/4 + 10;
+        }
+
+        /**
+         * Decode another block of input data.
+         *
+         * @return true if the state machine is still healthy.  false if
+         *         bad base-64 data has been detected in the input stream.
+         */
+        public boolean process(byte[] input, int offset, int len, boolean finish) {
+            if (this.state == 6) return false;
+
+            int p = offset;
+            len += offset;
+
+            // Using local variables makes the decoder about 12%
+            // faster than if we manipulate the member variables in
+            // the loop.  (Even alphabet makes a measurable
+            // difference, which is somewhat surprising to me since
+            // the member variable is final.)
+            int state = this.state;
+            int value = this.value;
+            int op = 0;
+            final byte[] output = this.output;
+            final int[] alphabet = this.alphabet;
+
+            while (p < len) {
+                // Try the fast path:  we're starting a new tuple and the
+                // next four bytes of the input stream are all data
+                // bytes.  This corresponds to going through states
+                // 0-1-2-3-0.  We expect to use this method for most of
+                // the data.
+                //
+                // If any of the next four bytes of input are non-data
+                // (whitespace, etc.), value will end up negative.  (All
+                // the non-data values in decode are small negative
+                // numbers, so shifting any of them up and or'ing them
+                // together will result in a value with its top bit set.)
+                //
+                // You can remove this whole block and the output should
+                // be the same, just slower.
+                if (state == 0) {
+                    while (p+4 <= len &&
+                           (value = ((alphabet[input[p] & 0xff] << 18) |
+                                     (alphabet[input[p+1] & 0xff] << 12) |
+                                     (alphabet[input[p+2] & 0xff] << 6) |
+                                     (alphabet[input[p+3] & 0xff]))) >= 0) {
+                        output[op+2] = (byte) value;
+                        output[op+1] = (byte) (value >> 8);
+                        output[op] = (byte) (value >> 16);
+                        op += 3;
+                        p += 4;
+                    }
+                    if (p >= len) break;
+                }
+
+                // The fast path isn't available -- either we've read a
+                // partial tuple, or the next four input bytes aren't all
+                // data, or whatever.  Fall back to the slower state
+                // machine implementation.
+
+                int d = alphabet[input[p++] & 0xff];
+
+                switch (state) {
+                case 0:
+                    if (d >= 0) {
+                        value = d;
+                        ++state;
+                    } else if (d != SKIP) {
+                        this.state = 6;
+                        return false;
+                    }
+                    break;
+
+                case 1:
+                    if (d >= 0) {
+                        value = (value << 6) | d;
+                        ++state;
+                    } else if (d != SKIP) {
+                        this.state = 6;
+                        return false;
+                    }
+                    break;
+
+                case 2:
+                    if (d >= 0) {
+                        value = (value << 6) | d;
+                        ++state;
+                    } else if (d == EQUALS) {
+                        // Emit the last (partial) output tuple;
+                        // expect exactly one more padding character.
+                        output[op++] = (byte) (value >> 4);
+                        state = 4;
+                    } else if (d != SKIP) {
+                        this.state = 6;
+                        return false;
+                    }
+                    break;
+
+                case 3:
+                    if (d >= 0) {
+                        // Emit the output triple and return to state 0.
+                        value = (value << 6) | d;
+                        output[op+2] = (byte) value;
+                        output[op+1] = (byte) (value >> 8);
+                        output[op] = (byte) (value >> 16);
+                        op += 3;
+                        state = 0;
+                    } else if (d == EQUALS) {
+                        // Emit the last (partial) output tuple;
+                        // expect no further data or padding characters.
+                        output[op+1] = (byte) (value >> 2);
+                        output[op] = (byte) (value >> 10);
+                        op += 2;
+                        state = 5;
+                    } else if (d != SKIP) {
+                        this.state = 6;
+                        return false;
+                    }
+                    break;
+
+                case 4:
+                    if (d == EQUALS) {
+                        ++state;
+                    } else if (d != SKIP) {
+                        this.state = 6;
+                        return false;
+                    }
+                    break;
+
+                case 5:
+                    if (d != SKIP) {
+                        this.state = 6;
+                        return false;
+                    }
+                    break;
+                }
+            }
+
+            if (!finish) {
+                // We're out of input, but a future call could provide
+                // more.
+                this.state = state;
+                this.value = value;
+                this.op = op;
+                return true;
+            }
+
+            // Done reading input.  Now figure out where we are left in
+            // the state machine and finish up.
+
+            switch (state) {
+            case 0:
+                // Output length is a multiple of three.  Fine.
+                break;
+            case 1:
+                // Read one extra input byte, which isn't enough to
+                // make another output byte.  Illegal.
+                this.state = 6;
+                return false;
+            case 2:
+                // Read two extra input bytes, enough to emit 1 more
+                // output byte.  Fine.
+                output[op++] = (byte) (value >> 4);
+                break;
+            case 3:
+                // Read three extra input bytes, enough to emit 2 more
+                // output bytes.  Fine.
+                output[op++] = (byte) (value >> 10);
+                output[op++] = (byte) (value >> 2);
+                break;
+            case 4:
+                // Read one padding '=' when we expected 2.  Illegal.
+                this.state = 6;
+                return false;
+            case 5:
+                // Read all the padding '='s we expected and no more.
+                // Fine.
+                break;
+            }
+
+            this.state = state;
+            this.op = op;
+            return true;
+        }
+    }
+
+    //  --------------------------------------------------------
+    //  encoding
+    //  --------------------------------------------------------
+
+    /**
+     * Base64-encode the given data and return a newly allocated
+     * String with the result.
+     *
+     * @param input  the data to encode
+     * @param flags  controls certain features of the encoded output.
+     *               Passing {@code DEFAULT} results in output that
+     *               adheres to RFC 2045.
+     */
+    public static String encodeToString(byte[] input, int flags) {
+        return new String(encode(input, flags), US_ASCII);
+    }
+
+    /**
+     * Base64-encode the given data and return a newly allocated
+     * String with the result.
+     *
+     * @param input  the data to encode
+     * @param offset the position within the input array at which to
+     *               start
+     * @param len    the number of bytes of input to encode
+     * @param flags  controls certain features of the encoded output.
+     *               Passing {@code DEFAULT} results in output that
+     *               adheres to RFC 2045.
+     */
+    public static String encodeToString(byte[] input, int offset, int len, int flags) {
+        return new String(encode(input, offset, len, flags), US_ASCII);
+    }
+
+    /**
+     * Base64-encode the given data and return a newly allocated
+     * byte[] with the result.
+     *
+     * @param input  the data to encode
+     * @param flags  controls certain features of the encoded output.
+     *               Passing {@code DEFAULT} results in output that
+     *               adheres to RFC 2045.
+     */
+    public static byte[] encode(byte[] input, int flags) {
+        return encode(input, 0, input.length, flags);
+    }
+
+    /**
+     * Base64-encode the given data and return a newly allocated
+     * byte[] with the result.
+     *
+     * @param input  the data to encode
+     * @param offset the position within the input array at which to
+     *               start
+     * @param len    the number of bytes of input to encode
+     * @param flags  controls certain features of the encoded output.
+     *               Passing {@code DEFAULT} results in output that
+     *               adheres to RFC 2045.
+     */
+    public static byte[] encode(byte[] input, int offset, int len, int flags) {
+        Encoder encoder = new Encoder(flags, null);
+
+        // Compute the exact length of the array we will produce.
+        int output_len = len / 3 * 4;
+
+        // Account for the tail of the data and the padding bytes, if any.
+        if (encoder.do_padding) {
+            if (len % 3 > 0) {
+                output_len += 4;
+            }
+        } else {
+            switch (len % 3) {
+                case 0: break;
+                case 1: output_len += 2; break;
+                case 2: output_len += 3; break;
+            }
+        }
+
+        // Account for the newlines, if any.
+        if (encoder.do_newline && len > 0) {
+            output_len += (((len-1) / (3 * Encoder.LINE_GROUPS)) + 1) *
+                (encoder.do_cr ? 2 : 1);
+        }
+
+        encoder.output = new byte[output_len];
+        encoder.process(input, offset, len, true);
+
+        assert encoder.op == output_len;
+
+        return encoder.output;
+    }
+
+    /* package */ static class Encoder extends Coder {
+        /**
+         * Emit a new line every this many output tuples.  Corresponds to
+         * a 76-character line length (the maximum allowable according to
+         * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>).
+         */
+        public static final int LINE_GROUPS = 19;
+
+        /**
+         * Lookup table for turning Base64 alphabet positions (6 bits)
+         * into output bytes.
+         */
+        private static final byte ENCODE[] = {
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+            'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
+        };
+
+        /**
+         * Lookup table for turning Base64 alphabet positions (6 bits)
+         * into output bytes.
+         */
+        private static final byte ENCODE_WEBSAFE[] = {
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+            'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_',
+        };
+
+        final private byte[] tail;
+        /* package */ int tailLen;
+        private int count;
+
+        final public boolean do_padding;
+        final public boolean do_newline;
+        final public boolean do_cr;
+        final private byte[] alphabet;
+
+        public Encoder(int flags, byte[] output) {
+            this.output = output;
+
+            do_padding = (flags & NO_PADDING) == 0;
+            do_newline = (flags & NO_WRAP) == 0;
+            do_cr = (flags & CRLF) != 0;
+            alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE;
+
+            tail = new byte[2];
+            tailLen = 0;
+
+            count = do_newline ? LINE_GROUPS : -1;
+        }
+
+        /**
+         * @return an overestimate for the number of bytes {@code
+         * len} bytes could encode to.
+         */
+        public int maxOutputSize(int len) {
+            return len * 8/5 + 10;
+        }
+
+        public boolean process(byte[] input, int offset, int len, boolean finish) {
+            // Using local variables makes the encoder about 9% faster.
+            final byte[] alphabet = this.alphabet;
+            final byte[] output = this.output;
+            int op = 0;
+            int count = this.count;
+
+            int p = offset;
+            len += offset;
+            int v = -1;
+
+            // First we need to concatenate the tail of the previous call
+            // with any input bytes available now and see if we can empty
+            // the tail.
+
+            switch (tailLen) {
+                case 0:
+                    // There was no tail.
+                    break;
+
+                case 1:
+                    if (p+2 <= len) {
+                        // A 1-byte tail with at least 2 bytes of
+                        // input available now.
+                        v = ((tail[0] & 0xff) << 16) |
+                            ((input[p++] & 0xff) << 8) |
+                            (input[p++] & 0xff);
+                        tailLen = 0;
+                    };
+                    break;
+
+                case 2:
+                    if (p+1 <= len) {
+                        // A 2-byte tail with at least 1 byte of input.
+                        v = ((tail[0] & 0xff) << 16) |
+                            ((tail[1] & 0xff) << 8) |
+                            (input[p++] & 0xff);
+                        tailLen = 0;
+                    }
+                    break;
+            }
+
+            if (v != -1) {
+                output[op++] = alphabet[(v >> 18) & 0x3f];
+                output[op++] = alphabet[(v >> 12) & 0x3f];
+                output[op++] = alphabet[(v >> 6) & 0x3f];
+                output[op++] = alphabet[v & 0x3f];
+                if (--count == 0) {
+                    if (do_cr) output[op++] = '\r';
+                    output[op++] = '\n';
+                    count = LINE_GROUPS;
+                }
+            }
+
+            // At this point either there is no tail, or there are fewer
+            // than 3 bytes of input available.
+
+            // The main loop, turning 3 input bytes into 4 output bytes on
+            // each iteration.
+            while (p+3 <= len) {
+                v = ((input[p] & 0xff) << 16) |
+                    ((input[p+1] & 0xff) << 8) |
+                    (input[p+2] & 0xff);
+                output[op] = alphabet[(v >> 18) & 0x3f];
+                output[op+1] = alphabet[(v >> 12) & 0x3f];
+                output[op+2] = alphabet[(v >> 6) & 0x3f];
+                output[op+3] = alphabet[v & 0x3f];
+                p += 3;
+                op += 4;
+                if (--count == 0) {
+                    if (do_cr) output[op++] = '\r';
+                    output[op++] = '\n';
+                    count = LINE_GROUPS;
+                }
+            }
+
+            if (finish) {
+                // Finish up the tail of the input.  Note that we need to
+                // consume any bytes in tail before any bytes
+                // remaining in input; there should be at most two bytes
+                // total.
+
+                if (p-tailLen == len-1) {
+                    int t = 0;
+                    v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4;
+                    tailLen -= t;
+                    output[op++] = alphabet[(v >> 6) & 0x3f];
+                    output[op++] = alphabet[v & 0x3f];
+                    if (do_padding) {
+                        output[op++] = '=';
+                        output[op++] = '=';
+                    }
+                    if (do_newline) {
+                        if (do_cr) output[op++] = '\r';
+                        output[op++] = '\n';
+                    }
+                } else if (p-tailLen == len-2) {
+                    int t = 0;
+                    v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) |
+                        (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2);
+                    tailLen -= t;
+                    output[op++] = alphabet[(v >> 12) & 0x3f];
+                    output[op++] = alphabet[(v >> 6) & 0x3f];
+                    output[op++] = alphabet[v & 0x3f];
+                    if (do_padding) {
+                        output[op++] = '=';
+                    }
+                    if (do_newline) {
+                        if (do_cr) output[op++] = '\r';
+                        output[op++] = '\n';
+                    }
+                } else if (do_newline && op > 0 && count != LINE_GROUPS) {
+                    if (do_cr) output[op++] = '\r';
+                    output[op++] = '\n';
+                }
+
+                assert tailLen == 0;
+                assert p == len;
+            } else {
+                // Save the leftovers in tail to be consumed on the next
+                // call to encodeInternal.
+
+                if (p == len-1) {
+                    tail[tailLen++] = input[p];
+                } else if (p == len-2) {
+                    tail[tailLen++] = input[p];
+                    tail[tailLen++] = input[p+1];
+                }
+            }
+
+            this.op = op;
+            this.count = count;
+
+            return true;
+        }
+    }
+
+    private Base64() { }   // don't instantiate
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Color.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Color.java
new file mode 100644 (file)
index 0000000..047cead
--- /dev/null
@@ -0,0 +1,27 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+/**
+ * Utilities for handling ARGB colors.
+ */
+public class Color
+{
+    public final static int CLEAR = 0x00000000;
+    public final static int WHITE = 0xFFFFFFFF;
+    public final static int BLACK = 0xFF000000;
+
+    public static float brightness (int argb) {
+        // TODO: we're ignoring alpha here...
+        int red = (argb >> 16) & 0xFF;
+        int green = (argb >> 8) & 0xFF;
+        int blue = (argb >> 0) & 0xFF;
+        int max = Math.max(Math.max(red, green), blue);
+        return ((float) max) / 255.0f;
+    }
+
+    private Color () {}
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Config.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Config.java
new file mode 100644 (file)
index 0000000..8767ae0
--- /dev/null
@@ -0,0 +1,477 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import static com.threerings.getdown.Log.log;
+
+/**
+ * Handles parsing and runtime access for Getdown's config files (mainly {@code getdown.txt}).
+ * These files contain zero or more mappings for a particular string key. Config values can be
+ * fetched as single strings, lists of strings, or parsed into primitives or compound data types
+ * like colors and rectangles.
+ */
+public class Config
+{
+    /** Empty configuration. */
+    public static final Config EMPTY = new Config(new HashMap<String, Object>());
+
+    /** Options that control the {@link #parsePairs} function. */
+    public static class ParseOpts {
+        // these should be tweaked as desired by the caller
+        public boolean biasToKey = false;
+        public boolean strictComments = false;
+
+        // these are filled in by parseConfig
+        public String osname = null;
+        public String osarch = null;
+    }
+
+    /**
+     * Creates a parse configuration, filling in the platform filters (or not) depending on the
+     * value of {@code checkPlatform}.
+     */
+    public static ParseOpts createOpts (boolean checkPlatform) {
+        ParseOpts opts = new ParseOpts();
+        if (checkPlatform) {
+            opts.osname = StringUtil.deNull(System.getProperty("os.name")).toLowerCase(Locale.ROOT);
+            opts.osarch = StringUtil.deNull(System.getProperty("os.arch")).toLowerCase(Locale.ROOT);
+        }
+        return opts;
+    }
+
+    /**
+     * Parses a configuration file containing key/value pairs. The file must be in the UTF-8
+     * encoding.
+     *
+     * @param opts options that influence the parsing. See {@link #createOpts}.
+     *
+     * @return a list of <code>String[]</code> instances containing the key/value pairs in the
+     * order they were parsed from the file.
+     */
+    public static List<String[]> parsePairs (File source, ParseOpts opts)
+        throws IOException
+    {
+        // annoyingly FileReader does not allow encoding to be specified (uses platform default)
+        try (FileInputStream fis = new FileInputStream(source);
+             InputStreamReader input = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
+            return parsePairs(input, opts);
+        }
+    }
+
+    /**
+     * See {@link #parsePairs(File,ParseOpts)}.
+     */
+    public static List<String[]> parsePairs (Reader source, ParseOpts opts) throws IOException
+    {
+        List<String[]> pairs = new ArrayList<>();
+        for (String line : FileUtil.readLines(source)) {
+            // nix comments
+            int cidx = line.indexOf("#");
+            if (opts.strictComments ? cidx == 0 : cidx != -1) {
+                line = line.substring(0, cidx);
+            }
+
+            // trim whitespace and skip blank lines
+            line = line.trim();
+            if (StringUtil.isBlank(line)) {
+                continue;
+            }
+
+            // parse our key/value pair
+            String[] pair = new String[2];
+            // if we're biasing toward key, put all the extra = in the key rather than the value
+            int eidx = opts.biasToKey ? line.lastIndexOf("=") : line.indexOf("=");
+            if (eidx != -1) {
+                pair[0] = line.substring(0, eidx).trim();
+                pair[1] = line.substring(eidx+1).trim();
+            } else {
+                pair[0] = line;
+                pair[1] = "";
+            }
+
+            // if the pair has an os qualifier, we need to process it
+            if (pair[1].startsWith("[")) {
+                int qidx = pair[1].indexOf("]");
+                if (qidx == -1) {
+                    log.warning("Bogus platform specifier", "key", pair[0], "value", pair[1]);
+                    continue; // omit the pair entirely
+                }
+                // if we're checking qualifiers and the os doesn't match this qualifier, skip it
+                String quals = pair[1].substring(1, qidx);
+                if (opts.osname != null && !checkQualifiers(quals, opts.osname, opts.osarch)) {
+                    log.debug("Skipping", "quals", quals,
+                              "osname", opts.osname, "osarch", opts.osarch,
+                              "key", pair[0], "value", pair[1]);
+                    continue;
+                }
+                // otherwise filter out the qualifier text
+                pair[1] = pair[1].substring(qidx+1).trim();
+            }
+
+            pairs.add(pair);
+        }
+
+        return pairs;
+    }
+
+    /**
+     * Takes a comma-separated String of four integers and returns a rectangle using those ints as
+     * the its x, y, width, and height.
+     */
+    public static Rectangle parseRect (String name, String value)
+    {
+        if (!StringUtil.isBlank(value)) {
+            int[] v = StringUtil.parseIntArray(value);
+            if (v != null && v.length == 4) {
+                return new Rectangle(v[0], v[1], v[2], v[3]);
+            }
+            log.warning("Ignoring invalid rect '" + name + "' config '" + value + "'.");
+        }
+        return null;
+    }
+
+    /**
+     * Parses the given hex color value (e.g. FFCC99) and returns an {@code Integer} with that
+     * value. If the given value is null or not a valid hexadecimal number, this will return null.
+     */
+    public static Integer parseColor (String hexValue)
+    {
+        if (!StringUtil.isBlank(hexValue)) {
+            try {
+                // if no alpha channel is specified, use 255 (full alpha)
+                int alpha = hexValue.length() > 6 ? 0 : 0xFF000000;
+                return Integer.parseInt(hexValue, 16) | alpha;
+            } catch (NumberFormatException e) {
+                log.warning("Ignoring invalid color", "hexValue", hexValue, "exception", e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Parses a configuration file containing key/value pairs. The file must be in the UTF-8
+     * encoding.
+     *
+     * @return a map from keys to values, where a value will be an array of strings if more than
+     * one key/value pair in the config file was associated with the same key.
+     */
+    public static Config parseConfig (File source, ParseOpts opts)
+        throws IOException
+    {
+        Map<String, Object> data = new HashMap<>();
+
+        // I thought that we could use HashMap<String, String[]> and put new String[] {pair[1]} for
+        // the null case, but it mysteriously dies on launch, so leaving it as HashMap<String,
+        // Object> for now
+        for (String[] pair : parsePairs(source, opts)) {
+            Object value = data.get(pair[0]);
+            if (value == null) {
+                data.put(pair[0], pair[1]);
+            } else if (value instanceof String) {
+                data.put(pair[0], new String[] { (String)value, pair[1] });
+            } else if (value instanceof String[]) {
+                String[] values = (String[])value;
+                String[] nvalues = new String[values.length+1];
+                System.arraycopy(values, 0, nvalues, 0, values.length);
+                nvalues[values.length] = pair[1];
+                data.put(pair[0], nvalues);
+            }
+        }
+
+        // special magic for the getdown.txt config: if the parsed data contains 'strict_comments =
+        // true' then we reparse the file with strict comments (i.e. # is only assumed to start a
+        // comment in column 0)
+        if (!opts.strictComments && Boolean.parseBoolean((String)data.get("strict_comments"))) {
+            opts.strictComments = true;
+            return parseConfig(source, opts);
+        }
+
+        return new Config(data);
+    }
+
+    public Config (Map<String,  Object> data) {
+        _data = data;
+    }
+
+    /**
+     * Returns whether {@code name} has a value in this config.
+     */
+    public boolean hasValue (String name) {
+        return _data.containsKey(name);
+    }
+
+    /**
+     * Returns the raw-value for {@code name}. This may be a {@code String}, {@code String[]}, or
+     * {@code null}.
+     */
+    public Object getRaw (String name) {
+        return _data.get(name);
+    }
+
+    /**
+     * Returns the specified config value as a string, or {@code null}.
+     */
+    public String getString (String name) {
+        return (String)_data.get(name);
+    }
+
+    /**
+     * Returns the specified config value as a string, or {@code def}.
+     */
+    public String getString (String name, String def) {
+        String value = (String)_data.get(name);
+        return value == null ? def : value;
+    }
+
+    /**
+     * Returns the specified config value as a boolean.
+     */
+    public boolean getBoolean (String name) {
+        return Boolean.parseBoolean(getString(name));
+    }
+
+    /**
+     * Massages a single string into an array and leaves existing array values as is. Simplifies
+     * access to parameters that are expected to be arrays.
+     */
+    public String[] getMultiValue (String name)
+    {
+        Object value = _data.get(name);
+        if (value == null) {
+          return new String[] {};
+        }
+        if (value instanceof String) {
+            return new String[] { (String)value };
+        } else {
+            return (String[])value;
+        }
+    }
+
+    /** Used to parse rectangle specifications from the config file. */
+    public Rectangle getRect (String name, Rectangle def)
+    {
+        String value = getString(name);
+        Rectangle rect = parseRect(name, value);
+        return (rect == null) ? def : rect;
+    }
+
+    /**
+     * Parses and returns the config value for {@code name} as an int. If no value is provided,
+     * {@code def} is returned. If the value is invalid, a warning is logged and {@code def} is
+     * returned.
+     */
+    public int getInt (String name, int def) {
+        String value = getString(name);
+        try {
+            return value == null ? def : Integer.parseInt(value);
+        } catch (Exception e) {
+            log.warning("Ignoring invalid int '" + name + "' config '" + value + "',");
+            return def;
+        }
+    }
+
+    /**
+     * Parses and returns the config value for {@code name} as a long. If no value is provided,
+     * {@code def} is returned. If the value is invalid, a warning is logged and {@code def} is
+     * returned.
+     */
+    public long getLong (String name, long def) {
+        String value = getString(name);
+        try {
+            return value == null ? def : Long.parseLong(value);
+        } catch (Exception e) {
+            log.warning("Ignoring invalid long '" + name + "' config '" + value + "',");
+            return def;
+        }
+    }
+
+    /** Used to parse color specifications from the config file. */
+    public int getColor (String name, int def)
+    {
+        String value = getString(name);
+        Integer color = parseColor(value);
+        return (color == null) ? def : color;
+    }
+
+    /** Parses a list of strings from the config file. */
+    public String[] getList (String name)
+    {
+        String value = getString(name);
+        return (value == null) ? new String[0] : StringUtil.parseStringArray(value);
+    }
+
+    /**
+     * Parses a URL from the config file, checking first for a localized version.
+     */
+    public String getUrl (String name, String def)
+    {
+        String value = getString(name + "." + Locale.getDefault().getLanguage());
+        if (StringUtil.isBlank(value)) {
+            value = getString(name);
+        }
+        if (StringUtil.isBlank(value)) {
+            value = def;
+        }
+        if (!StringUtil.isBlank(value)) {
+            try {
+                HostWhitelist.verify(new URL(value));
+            } catch (MalformedURLException e) {
+                log.warning("Invalid URL.", "url", value, e);
+                value = null;
+            }
+        }
+        return value;
+    }
+
+    /**
+     * A helper function for {@link #parsePairs(Reader,ParseOpts)}. Qualifiers have the following
+     * form:
+     * <pre>
+     * id = os[-arch]
+     * ids = id | id,ids
+     * quals = !id | ids
+     * </pre>
+     * Examples: [linux-amd64,linux-x86_64], [windows], [mac os x], [!windows]. Negative qualifiers
+     * must appear alone, they cannot be used with other qualifiers (positive or negative).
+     */
+    protected static boolean checkQualifiers (String quals, String osname, String osarch)
+    {
+        if (quals.startsWith("!")) {
+            if (quals.indexOf(",") != -1) { // sanity check
+                log.warning("Multiple qualifiers cannot be used when one of the qualifiers " +
+                            "is negative", "quals", quals);
+                return false;
+            }
+            return !checkQualifier(quals.substring(1), osname, osarch);
+        }
+        for (String qual : quals.split(",")) {
+            if (checkQualifier(qual, osname, osarch)) {
+                return true; // if we have a positive match, we can immediately return true
+            }
+        }
+        return false; // we had no positive matches, so return false
+    }
+
+    /** A helper function for {@link #checkQualifiers}. */
+    protected static boolean checkQualifier (String qual, String osname, String osarch)
+    {
+        String[] bits = qual.trim().toLowerCase(Locale.ROOT).split("-");
+        String os = bits[0], arch = (bits.length > 1) ? bits[1] : "";
+        return (osname.indexOf(os) != -1) && (osarch.indexOf(arch) != -1);
+    }
+    
+    public void mergeConfig(Config newValues, boolean merge) {
+      
+      for (Map.Entry<String, Object> entry : newValues.getData().entrySet()) {
+        
+        String key = entry.getKey();
+        Object nvalue = entry.getValue();
+
+        String mkey = key.indexOf('.') > -1 ? key.substring(key.indexOf('.') + 1) : key;
+        if (merge && allowedMergeKeys.contains(mkey)) {
+          
+          // merge multi values
+          
+          Object value = _data.get(key);
+          
+          if (value == null) {
+            _data.put(key, nvalue);
+          } else if (value instanceof String) {
+            if (nvalue instanceof String) {
+              
+              // value is String, nvalue is String
+              _data.put(key, new String[] { (String)value, (String)nvalue });
+              
+            } else if (nvalue instanceof String[]) {
+              
+              // value is String, nvalue is String[]
+              String[] nvalues = (String[])nvalue;
+              String[] newvalues = new String[nvalues.length+1];
+              newvalues[0] = (String)value;
+              System.arraycopy(nvalues, 0, newvalues, 1, nvalues.length);
+              _data.put(key, newvalues);
+              
+            }
+          } else if (value instanceof String[]) {
+            if (nvalue instanceof String) {
+              
+              // value is String[], nvalue is String
+              String[] values = (String[])value;
+              String[] newvalues = new String[values.length+1];
+              System.arraycopy(values, 0, newvalues, 0, values.length);
+              newvalues[values.length] = (String)nvalue;
+              _data.put(key, newvalues);
+              
+            } else if (nvalue instanceof String[]) {
+              
+              // value is String[], nvalue is String[]
+              String[] values = (String[])value;
+              String[] nvalues = (String[])nvalue;
+              String[] newvalues = new String[values.length + nvalues.length];
+              System.arraycopy(values, 0, newvalues, 0, values.length);
+              System.arraycopy(nvalues, 0, newvalues, values.length, newvalues.length);
+              _data.put(key, newvalues);
+              
+            }
+          }
+          
+        } else if (allowedReplaceKeys.contains(mkey)){
+          
+          // replace value
+          _data.put(key, nvalue);
+          
+        } else {
+          log.warning("Not merging key '"+key+"' into config");
+        }
+
+      }
+      
+    }
+    
+    public String toString() {
+      StringBuilder sb = new StringBuilder();
+      for (Map.Entry<String, Object> entry : getData().entrySet()) {
+        String key = entry.getKey();
+        Object val = entry.getValue();
+        sb.append(key);
+        sb.append("=");
+        if (val instanceof String) {
+          sb.append((String)val);
+        } else if (val instanceof String[]) {
+          sb.append(Arrays.toString((String[])val));
+        } else {
+          sb.append("Value not String or String[]");
+        }
+        sb.append("\n");
+      }
+      return sb.toString();
+    }
+    
+    public Map<String, Object> getData() {
+      return _data;
+    }
+
+    private final Map<String, Object> _data;
+    public static final List<String> allowedReplaceKeys = Arrays.asList("appbase","apparg","jvmarg","jvmmempc"); // these are the ones we might use
+    public static final List<String> allowedMergeKeys = Arrays.asList("apparg","jvmarg"); // these are the ones we might use
+    //private final List<String> allowedMergeKeys = Arrays.asList("apparg","jvmarg","resource","code","java_location"); // (not exhaustive list here)
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java
new file mode 100644 (file)
index 0000000..21b0569
--- /dev/null
@@ -0,0 +1,73 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLDecoder;
+
+import com.threerings.getdown.data.SysProps;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class ConnectionUtil
+{
+    /**
+     * Opens a connection to a URL, setting the authentication header if user info is present.
+     * @param proxy the proxy via which to perform HTTP connections.
+     * @param url the URL to which to open a connection.
+     * @param connectTimeout if {@code > 0} then a timeout, in seconds, to use when opening the
+     * connection. If {@code 0} is supplied, the connection timeout specified via system properties
+     * will be used instead.
+     * @param readTimeout if {@code > 0} then a timeout, in seconds, to use while reading data from
+     * the connection. If {@code 0} is supplied, the read timeout specified via system properties
+     * will be used instead.
+     */
+    public static URLConnection open (Proxy proxy, URL url, int connectTimeout, int readTimeout)
+        throws IOException
+    {
+        URLConnection conn = url.openConnection(proxy);
+
+        // configure a connect timeout, if requested
+        int ctimeout = connectTimeout > 0 ? connectTimeout : SysProps.connectTimeout();
+        if (ctimeout > 0) {
+            conn.setConnectTimeout(ctimeout * 1000);
+        }
+
+        // configure a read timeout, if requested
+        int rtimeout = readTimeout > 0 ? readTimeout : SysProps.readTimeout();
+        if (rtimeout > 0) {
+            conn.setReadTimeout(rtimeout * 1000);
+        }
+
+        // If URL has a username:password@ before hostname, use HTTP basic auth
+        String userInfo = url.getUserInfo();
+        if (userInfo != null) {
+            // Remove any percent-encoding in the username/password
+            userInfo = URLDecoder.decode(userInfo, "UTF-8");
+            // Now base64 encode the auth info and make it a single line
+            String encoded = Base64.encodeToString(userInfo.getBytes(UTF_8), Base64.DEFAULT).
+                replaceAll("\\n","").replaceAll("\\r", "");
+            conn.setRequestProperty("Authorization", "Basic " + encoded);
+        }
+
+        return conn;
+    }
+
+    /**
+     * Opens a connection to a http or https URL, setting the authentication header if user info is
+     * present. Throws a class cast exception if the connection returned is not the right type. See
+     * {@link #open} for parameter documentation.
+     */
+    public static HttpURLConnection openHttp (
+        Proxy proxy, URL url, int connectTimeout, int readTimeout) throws IOException
+    {
+        return (HttpURLConnection)open(proxy, url, connectTimeout, readTimeout);
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/FileUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/FileUtil.java
new file mode 100644 (file)
index 0000000..67d0330
--- /dev/null
@@ -0,0 +1,316 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.jar.*;
+import java.util.zip.GZIPInputStream;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
+import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
+
+import com.threerings.getdown.util.StreamUtil;
+import com.threerings.getdown.Log;
+import static com.threerings.getdown.Log.log;
+
+/**
+ * File related utilities.
+ */
+public class FileUtil
+{
+    /**
+     * Gets the specified source file to the specified destination file by hook or crook. Windows
+     * has all sorts of problems which we work around in this method.
+     *
+     * @return true if we managed to get the job done, false otherwise.
+     */
+    public static boolean renameTo (File source, File dest)
+    {
+        // if we're on a civilized operating system we may be able to simple rename it
+        if (source.renameTo(dest)) {
+            return true;
+        }
+
+        // fall back to trying to rename the old file out of the way, rename the new file into
+        // place and then delete the old file
+        if (dest.exists()) {
+            File temp = new File(dest.getPath() + "_old");
+            if (temp.exists() && !deleteHarder(temp)) {
+                log.warning("Failed to delete old intermediate file " + temp + ".");
+                // the subsequent code will probably fail
+            }
+            if (dest.renameTo(temp) && source.renameTo(dest)) {
+                if (!deleteHarder(temp)) {
+                    log.warning("Failed to delete intermediate file " + temp + ".");
+                }
+                return true;
+            }
+        }
+
+        // as a last resort, try copying the old data over the new
+        try {
+            copy(source, dest);
+        } catch (IOException ioe) {
+            log.warning("Failed to copy " + source + " to " + dest + ": " + ioe);
+            return false;
+        }
+
+        if (!deleteHarder(source)) {
+            log.warning("Failed to delete " + source + " after brute force copy to " + dest + ".");
+        }
+        return true;
+    }
+
+    /**
+     * "Tries harder" to delete {@code file} than just calling {@code delete} on it. Presently this
+     * just means "try a second time if the first time fails, and if that fails then try to delete
+     * when the virtual machine terminates." On Windows Vista, sometimes deletes fail but then
+     * succeed if you just try again. Given that delete failure is a rare occurrence, we can
+     * implement this hacky workaround without any negative consequences for normal behavior.
+     */
+    public static boolean deleteHarder (File file) {
+        // if at first you don't succeed... try, try again
+        boolean deleted = (file.delete() || file.delete());
+        if (!deleted) {
+            file.deleteOnExit();
+        }
+        return deleted;
+    }
+
+    /**
+     * Force deletes {@code file} and all of its children recursively using {@link #deleteHarder}.
+     * Note that some children may still be deleted even if {@code false} is returned. Also, since
+     * {@link #deleteHarder} is used, the {@code file} could be deleted once the jvm exits even if
+     * {@code false} is returned.
+     *
+     * @param file file to delete.
+     * @return true iff {@code file} was successfully deleted.
+     */
+    public static boolean deleteDirHarder (File file) {
+        if (file.isDirectory()) {
+            for (File child : file.listFiles()) {
+                deleteDirHarder(child);
+            }
+        }
+        return deleteHarder(file);
+    }
+
+    /**
+     * Reads the contents of the supplied input stream into a list of lines. Closes the reader on
+     * successful or failed completion.
+     */
+    public static List<String> readLines (Reader in)
+        throws IOException
+    {
+        List<String> lines = new ArrayList<>();
+        try (BufferedReader bin = new BufferedReader(in)) {
+            for (String line = null; (line = bin.readLine()) != null; lines.add(line)) {}
+        }
+        return lines;
+    }
+
+    /**
+     * Unpacks the specified jar file into the specified target directory.
+     * @param cleanExistingDirs if true, all files in all directories contained in {@code jar} will
+     * be deleted prior to unpacking the jar.
+     */
+    public static void unpackJar (JarFile jar, File target, boolean cleanExistingDirs)
+        throws IOException
+    {
+        if (cleanExistingDirs) {
+            Enumeration<?> entries = jar.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry entry = (JarEntry)entries.nextElement();
+                if (entry.isDirectory()) {
+                    File efile = new File(target, entry.getName());
+                    if (efile.exists()) {
+                        for (File f : efile.listFiles()) {
+                            if (!f.isDirectory())
+                            f.delete();
+                        }
+                    }
+                }
+            }
+        }
+
+        Enumeration<?> entries = jar.entries();
+        while (entries.hasMoreElements()) {
+            JarEntry entry = (JarEntry)entries.nextElement();
+            File efile = new File(target, entry.getName());
+
+            // if we're unpacking a normal jar file, it will have special path
+            // entries that allow us to create our directories first
+            if (entry.isDirectory()) {
+                if (!efile.exists() && !efile.mkdir()) {
+                    log.warning("Failed to create jar entry path", "jar", jar, "entry", entry);
+                }
+                continue;
+            }
+
+            // but some do not, so we want to ensure that our directories exist
+            // prior to getting down and funky
+            File parent = new File(efile.getParent());
+            if (!parent.exists() && !parent.mkdirs()) {
+                log.warning("Failed to create jar entry parent", "jar", jar, "parent", parent);
+                continue;
+            }
+
+            try (BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(efile));
+                InputStream jin = jar.getInputStream(entry)) {
+                StreamUtil.copy(jin, fout);
+            } catch (Exception e) {
+                throw new IOException(
+                    Log.format("Failure unpacking", "jar", jar, "entry", efile), e);
+            }
+        }
+    }
+
+    /**
+     * Unpacks the specified tgz file into the specified target directory.
+     * @param cleanExistingDirs if true, all files in all directories contained in {@code tgz} will
+     * be deleted prior to unpacking the tgz.
+     */
+    public static void unpackTgz (TarArchiveInputStream tgz, File target, boolean cleanExistingDirs)
+        throws IOException
+    {
+               TarArchiveEntry entry;
+               while ((entry = tgz.getNextTarEntry()) != null) {
+            // sanitize the entry name
+                       String entryName = entry.getName();
+                       if (entryName.startsWith(File.separator))
+                       {
+                               entryName = entryName.substring(File.separator.length());
+                       }
+            File efile = new File(target, entryName);
+
+            // if we're unpacking a normal tgz file, it will have special path
+            // entries that allow us to create our directories first
+            if (entry.isDirectory()) {
+               
+                               if (cleanExistingDirs) {
+                    if (efile.exists()) {
+                        for (File f : efile.listFiles()) {
+                            if (!f.isDirectory())
+                            f.delete();
+                        }
+                    }
+                               }
+                               
+                if (!efile.exists() && !efile.mkdir()) {
+                    log.warning("Failed to create tgz entry path", "tgz", tgz, "entry", entry);
+                }
+                continue;
+            }
+
+            // but some do not, so we want to ensure that our directories exist
+            // prior to getting down and funky
+            File parent = new File(efile.getParent());
+            if (!parent.exists() && !parent.mkdirs()) {
+                log.warning("Failed to create tgz entry parent", "tgz", tgz, "parent", parent);
+                continue;
+            }
+
+            if (entry.isLink())
+            {
+               System.out.println("Creating hard link "+efile.getName()+" -> "+entry.getLinkName());
+               Files.createLink(efile.toPath(), Paths.get(entry.getLinkName()));
+               continue;
+            }
+
+            if (entry.isSymbolicLink())
+            {
+               System.out.println("Creating symbolic link "+efile.getName()+" -> "+entry.getLinkName());
+               Files.createSymbolicLink(efile.toPath(), Paths.get(entry.getLinkName()));
+               continue;
+            }
+            
+            try (BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(efile));
+                InputStream tin = tgz;) {
+                StreamUtil.copy(tin, fout);
+            } catch (Exception e) {
+                throw new IOException(
+                    Log.format("Failure unpacking", "tgz", tgz, "entry", efile), e);
+            }
+        }
+    }
+
+    /**
+     * Unpacks a pack200 packed jar file from {@code packedJar} into {@code target}. If {@code
+     * packedJar} has a {@code .gz} extension, it will be gunzipped first.
+     */
+    public static void unpackPacked200Jar (File packedJar, File target) throws IOException
+    {
+        try (InputStream packJarIn = new FileInputStream(packedJar);
+             JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(target))) {
+            boolean gz = (packedJar.getName().endsWith(".gz") ||
+                          packedJar.getName().endsWith(".gz_new"));
+            try (InputStream packJarIn2 = (gz ? new GZIPInputStream(packJarIn) : packJarIn)) {
+                Pack200.Unpacker unpacker = Pack200.newUnpacker();
+                unpacker.unpack(packJarIn2, jarOut);
+            }
+        }
+    }
+
+    /**
+     * Copies the given {@code source} file to the given {@code target}.
+     */
+    public static void copy (File source, File target) throws IOException {
+        try (FileInputStream in = new FileInputStream(source);
+             FileOutputStream out = new FileOutputStream(target)) {
+            StreamUtil.copy(in, out);
+        }
+    }
+
+    /**
+     * Marks {@code file} as executable, if it exists. Catches and logs any errors that occur.
+     */
+    public static void makeExecutable (File file) {
+        try {
+            if (file.exists()) {
+                if (!file.setExecutable(true, false)) {
+                    log.warning("Failed to mark as executable", "file", file);
+                }
+            }
+        } catch (Exception e) {
+            log.warning("Failed to mark as executable", "file", file, "error", e);
+        }
+    }
+
+    /**
+     * Used by {@link #walkTree}.
+     */
+    public interface Visitor
+    {
+        void visit (File file);
+    }
+
+    /**
+     * Walks all files in {@code root}, calling {@code visitor} on each file in the tree.
+     */
+    public static void walkTree (File root, Visitor visitor)
+    {
+        File[] children = root.listFiles();
+        if (children == null) return;
+        Deque<File> stack = new ArrayDeque<>(Arrays.asList(children));
+        while (!stack.isEmpty()) {
+            File currentFile = stack.pop();
+            if (currentFile.exists()) {
+                visitor.visit(currentFile);
+                File[] currentChildren = currentFile.listFiles();
+                if (currentChildren != null) {
+                    for (File file : currentChildren) {
+                        stack.push(file);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/HostWhitelist.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/HostWhitelist.java
new file mode 100644 (file)
index 0000000..ff0661b
--- /dev/null
@@ -0,0 +1,63 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import com.threerings.getdown.data.Build;
+
+/**
+ * Optional support for compiling a URL host whitelist into the Getdown JAR.
+ * Useful if you're on the paranoid end of the security spectrum.
+ *
+ * @see Build#hostWhitelist()
+ */
+public final class HostWhitelist
+{
+    /**
+     * Verifies that the specified URL should be accessible, per the built-in host whitelist.
+     * See {@link Build#hostWhitelist()} and {@link #verify(List,URL)}.
+     */
+    public static URL verify (URL url) throws MalformedURLException
+    {
+        
+      
+        return verify(Build.hostWhitelist(), url);
+    }
+
+    /**
+     * Verifies that the specified URL should be accessible, per the supplied host whitelist.
+     * If the URL should not be accessible, this method throws a {@link MalformedURLException}.
+     * If the URL should be accessible, this method simply returns the {@link URL} passed in.
+     */
+    public static URL verify (List<String> hosts, URL url) throws MalformedURLException
+    {
+        if (url == null || hosts.isEmpty()) {
+            // either there is no URL to validate or no whitelist was configured
+            return url;
+        }
+
+        String urlHost = url.getHost();
+        String protocol = url.getProtocol();
+        
+        if (ALLOW_LOCATOR_FILE_PROTOCOL && protocol.equals("file") && urlHost.equals("")) {
+          return url;
+        }
+        
+        for (String host : hosts) {
+            String regex = host.replace(".", "\\.").replace("*", ".*");
+            if (urlHost.matches(regex)) {
+                return url;
+            }
+        }
+
+        throw new MalformedURLException(
+            "The host for the specified URL (" + url + ") is not in the host whitelist: " + hosts);
+    }
+    private static final boolean ALLOW_LOCATOR_FILE_PROTOCOL = Build.allowLocatorFileProtocol();
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java
new file mode 100644 (file)
index 0000000..ae493bb
--- /dev/null
@@ -0,0 +1,280 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+import javax.xml.bind.DatatypeConverter;
+
+import java.security.MessageDigest;
+
+import static com.threerings.getdown.Log.log;
+
+/**
+ * Useful routines for launching Java applications from within other Java
+ * applications.
+ */
+public class LaunchUtil
+{
+    /** The directory into which a local VM installation should be unpacked. */
+    public static final String LOCAL_JAVA_DIR = "jre";
+
+    /**
+     * Writes a <code>version.txt</code> file into the specified application directory and
+     * attempts to relaunch Getdown in that directory which will cause it to upgrade to the newly
+     * specified version and relaunch the application.
+     *
+     * @param appdir the directory in which the application is installed.
+     * @param getdownJarName the name of the getdown jar file in the application directory. This is
+     * probably <code>getdown-pro.jar</code> or <code>getdown-retro-pro.jar</code> if you are using
+     * the results of the standard build.
+     * @param newVersion the new version to which Getdown will update when it is executed.
+     *
+     * @return true if the relaunch succeeded, false if we were unable to relaunch due to being on
+     * Windows 9x where we cannot launch subprocesses without waiting around for them to exit,
+     * reading their stdout and stderr all the while. If true is returned, the application may exit
+     * after making this call as it will be upgraded and restarted. If false is returned, the
+     * application should tell the user that they must restart the application manually.
+     *
+     * @exception IOException thrown if we were unable to create the <code>version.txt</code> file
+     * in the supplied application directory. If the version.txt file cannot be created, restarting
+     * Getdown will not cause the application to be upgraded, so the application will have to
+     * resort to telling the user that it is in a bad way.
+     */
+    public static boolean updateVersionAndRelaunch (
+            File appdir, String getdownJarName, String newVersion)
+        throws IOException
+    {
+        // create the file that instructs Getdown to upgrade
+        File vfile = new File(appdir, "version.txt");
+        try (PrintStream ps = new PrintStream(new FileOutputStream(vfile))) {
+            ps.println(newVersion);
+        }
+
+        // make sure that we can find our getdown.jar file and can safely launch children
+        File pro = new File(appdir, getdownJarName);
+        if (mustMonitorChildren() || !pro.exists()) {
+            return false;
+        }
+
+        // do the deed
+        String[] args = new String[] {
+            getJVMPath(appdir), "-jar", pro.toString(), appdir.getPath()
+        };
+        log.info("Running " + StringUtil.join(args, "\n  "));
+        try {
+            Runtime.getRuntime().exec(args, null);
+            return true;
+        } catch (IOException ioe) {
+            log.warning("Failed to run getdown", ioe);
+            return false;
+        }
+    }
+
+    /**
+     * Reconstructs the path to the JVM used to launch this process.
+     */
+    public static String getJVMPath (File appdir)
+    {
+        return getJVMPath(appdir, false);
+    }
+
+    /**
+     * Reconstructs the path to the JVM used to launch this process.
+     *
+     * @param windebug if true we will use java.exe instead of javaw.exe on Windows.
+     */
+    public static String getJVMPath (File appdir, boolean windebug)
+    {
+        // first look in our application directory for an installed VM
+        String vmpath = checkJVMPath(new File(appdir, LOCAL_JAVA_DIR).getAbsolutePath(), windebug);
+        if (vmpath == null && isMacOS()) {
+                       vmpath = checkJVMPath(new File(appdir, LOCAL_JAVA_DIR + "/Contents/Home").getAbsolutePath(), windebug);
+        }
+
+        // then fall back to the VM in which we're already running
+        if (vmpath == null) {
+            vmpath = checkJVMPath(System.getProperty("java.home"), windebug);
+        }
+
+        // then throw up our hands and hope for the best
+        if (vmpath == null) {
+            log.warning("Unable to find java [appdir=" + appdir +
+                        ", java.home=" + System.getProperty("java.home") + "]!");
+            vmpath = "java";
+        }
+
+        // Oddly, the Mac OS X specific java flag -Xdock:name will only work if java is launched
+        // from /usr/bin/java, and not if launched by directly referring to <java.home>/bin/java,
+        // even though the former is a symlink to the latter! To work around this, see if the
+        // desired jvm is in fact pointed to by /usr/bin/java and, if so, use that instead.
+        if (isMacOS()) {
+            try {
+                File localVM = new File("/usr/bin/java").getCanonicalFile();
+                if (localVM.equals(new File(vmpath).getCanonicalFile())) {
+                    vmpath = "/usr/bin/java";
+                }
+            } catch (IOException ioe) {
+                log.warning("Failed to check Mac OS canonical VM path.", ioe);
+            }
+        }
+
+        return vmpath;
+    }
+
+    private static String _getMD5FileChecksum (File file) {
+       // check md5 digest
+       String algo = "MD5";
+       String checksum = "";
+       try {
+               MessageDigest md = MessageDigest.getInstance(algo);
+               md.update(Files.readAllBytes(Paths.get(file.getAbsolutePath())));
+               byte[] digest = md.digest();
+               checksum = DatatypeConverter.printHexBinary(digest).toUpperCase();
+       } catch (Exception e) {
+               System.out.println("Couldn't create "+algo+" digest of "+file.getPath());
+       }
+       return checksum;
+    }
+    
+    /**
+     * Upgrades Getdown by moving an installation managed copy of the Getdown jar file over the
+     * non-managed copy (which would be used to run Getdown itself).
+     *
+     * <p> If the upgrade fails for a variety of reasons, warnings are logged but no other actions
+     * are taken. There's not much else one can do other than try again next time around.
+     */
+    public static void upgradeGetdown (File oldgd, File curgd, File newgd)
+    {
+        // we assume getdown's jar file size changes with every upgrade, this is not guaranteed,
+        // but in reality it will, and it allows us to avoid pointlessly upgrading getdown every
+        // time the client is updated which is unnecessarily flirting with danger
+        if (!newgd.exists())
+        {
+            return;
+        }
+        
+        if (newgd.length() == curgd.length()) {
+               if (_getMD5FileChecksum(newgd).equals(_getMD5FileChecksum(curgd)))
+               {
+                               return;
+               }
+        }
+
+        log.info("Updating Getdown with " + newgd + "...");
+
+        // clear out any old getdown
+        if (oldgd.exists()) {
+            FileUtil.deleteHarder(oldgd);
+        }
+
+        // now try updating using renames
+        if (!curgd.exists() || curgd.renameTo(oldgd)) {
+            if (newgd.renameTo(curgd)) {
+                FileUtil.deleteHarder(oldgd); // yay!
+                try {
+                    // copy the moved file back to getdown-dop-new.jar so that we don't end up
+                    // downloading another copy next time
+                    FileUtil.copy(curgd, newgd);
+                } catch (IOException e) {
+                    log.warning("Error copying updated Getdown back: " + e);
+                }
+                return;
+            }
+
+            log.warning("Unable to renameTo(" + oldgd + ").");
+            // try to unfuck ourselves
+            if (!oldgd.renameTo(curgd)) {
+                log.warning("Oh God, why dost thee scorn me so.");
+            }
+        }
+
+        // that didn't work, let's try copying it
+        log.info("Attempting to upgrade by copying over " + curgd + "...");
+        try {
+            FileUtil.copy(newgd, curgd);
+        } catch (IOException ioe) {
+            log.warning("Mayday! Brute force copy method also failed.", ioe);
+        }
+    }
+
+    /**
+     * Returns true if, on this operating system, we have to stick around and read the stderr from
+     * our children processes to prevent them from filling their output buffers and hanging.
+     */
+    public static boolean mustMonitorChildren ()
+    {
+        String osname = System.getProperty("os.name", "").toLowerCase(Locale.ROOT);
+        return (osname.indexOf("windows 98") != -1 || osname.indexOf("windows me") != -1);
+    }
+
+    /**
+     * Returns true if we're running in a JVM that identifies its operating system as Windows.
+     */
+    public static final boolean isWindows () { return _isWindows; }
+
+    /**
+     * Returns true if we're running in a JVM that identifies its operating system as MacOS.
+     */
+    public static final boolean isMacOS () { return _isMacOS; }
+
+    /**
+     * Returns true if we're running in a JVM that identifies its operating system as Linux.
+     */
+    public static final boolean isLinux () { return _isLinux; }
+
+    /**
+     * Checks whether a Java Virtual Machine can be located in the supplied path.
+     */
+    protected static String checkJVMPath (String vmhome, boolean windebug)
+    {
+        String vmbase = vmhome + File.separator + "bin" + File.separator;
+        String vmpath = vmbase + "java";
+        if (new File(vmpath).exists()) {
+            return vmpath;
+        }
+
+        if (!windebug) {
+            vmpath = vmbase + "javaw.exe";
+            if (new File(vmpath).exists()) {
+                return vmpath;
+            }
+        }
+
+        vmpath = vmbase + "java.exe";
+        if (new File(vmpath).exists()) {
+            return vmpath;
+        }
+
+        return null;
+    }
+
+    /** Flag indicating that we're on Windows; initialized when this class is first loaded. */
+    protected static boolean _isWindows;
+    /** Flag indicating that we're on MacOS; initialized when this class is first loaded. */
+    protected static boolean _isMacOS;
+    /** Flag indicating that we're on Linux; initialized when this class is first loaded. */
+    protected static boolean _isLinux;
+
+    static {
+        try {
+            String osname = System.getProperty("os.name");
+            osname = (osname == null) ? "" : osname;
+            _isWindows = (osname.indexOf("Windows") != -1);
+            _isMacOS = (osname.indexOf("Mac OS") != -1 ||
+                        osname.indexOf("MacOS") != -1);
+            _isLinux = (osname.indexOf("Linux") != -1);
+        } catch (Exception e) {
+            // can't grab system properties; we'll just pretend we're not on any of these OSes
+        }
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/MessageUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/MessageUtil.java
new file mode 100644 (file)
index 0000000..28dbdcf
--- /dev/null
@@ -0,0 +1,144 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+public class MessageUtil {
+
+    /**
+     * Returns whether or not the provided string is tainted. See {@link #taint}. Null strings
+     * are considered untainted.
+     */
+    public static boolean isTainted (String text)
+    {
+        return text != null && text.startsWith(TAINT_CHAR);
+    }
+
+    /**
+     * Call this to "taint" any string that has been entered by an entity outside the application
+     * so that the translation code knows not to attempt to translate this string when doing
+     * recursive translations.
+     */
+    public static String taint (Object text)
+    {
+        return TAINT_CHAR + text;
+    }
+
+    /**
+     * Removes the tainting character added to a string by {@link #taint}. If the provided string
+     * is not tainted, this silently returns the originally provided string.
+     */
+    public static String untaint (String text)
+    {
+        return isTainted(text) ? text.substring(TAINT_CHAR.length()) : text;
+    }
+
+    /**
+     * Composes a message key with an array of arguments. The message can subsequently be
+     * decomposed and translated without prior knowledge of how many arguments were provided.
+     */
+    public static String compose (String key, Object... args)
+    {
+        StringBuilder buf = new StringBuilder();
+        buf.append(key);
+        buf.append('|');
+        for (int i = 0; i < args.length; i++) {
+            if (i > 0) {
+                buf.append('|');
+            }
+            // escape the string while adding to the buffer
+            String arg = (args[i] == null) ? "" : String.valueOf(args[i]);
+            int alength = arg.length();
+            for (int p = 0; p < alength; p++) {
+                char ch = arg.charAt(p);
+                if (ch == '|') {
+                    buf.append("\\!");
+                } else if (ch == '\\') {
+                    buf.append("\\\\");
+                } else {
+                    buf.append(ch);
+                }
+            }
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Compose a message with String args. This is just a convenience so callers do not have to
+     * cast their String[] to an Object[].
+     */
+    public static String compose (String key, String... args)
+    {
+        return compose(key, (Object[]) args);
+    }
+
+    /**
+     * A convenience method for calling {@link #compose(String,Object[])} with an array of
+     * arguments that will be automatically tainted (see {@link #taint}).
+     */
+    public static String tcompose (String key, Object... args)
+    {
+        int acount = args.length;
+        String[] targs = new String[acount];
+        for (int ii = 0; ii < acount; ii++) {
+            targs[ii] = taint(args[ii]);
+        }
+        return compose(key, (Object[]) targs);
+    }
+
+    /**
+     * A convenience method for calling {@link #compose(String,String[])} with an array of argument
+     * that will be automatically tainted.
+     */
+    public static String tcompose (String key, String... args)
+    {
+        for (int ii = 0, nn = args.length; ii < nn; ii++) {
+            args[ii] = taint(args[ii]);
+        }
+        return compose(key, args);
+    }
+
+    /**
+     * Used to escape single quotes so that they are not interpreted by <code>MessageFormat</code>.
+     * As we assume all single quotes are to be escaped, we cannot use the characters
+     * <code>{</code> and <code>}</code> in our translation strings, but this is a small price to
+     * pay to have to differentiate between messages that will and won't eventually be parsed by a
+     * <code>MessageFormat</code> instance.
+     */
+    public static String escape (String message)
+    {
+        return message.replace("'", "''");
+    }
+
+    /**
+     * Unescapes characters that are escaped in a call to compose.
+     */
+    public static String unescape (String value)
+    {
+        int bsidx = value.indexOf('\\');
+        if (bsidx == -1) {
+            return value;
+        }
+
+        StringBuilder buf = new StringBuilder();
+        int vlength = value.length();
+        for (int ii = 0; ii < vlength; ii++) {
+            char ch = value.charAt(ii);
+            if (ch != '\\' || ii == vlength-1) {
+                buf.append(ch);
+            } else {
+                // look at the next character
+                ch = value.charAt(++ii);
+                buf.append((ch == '!') ? '|' : ch);
+            }
+        }
+
+        return buf.toString();
+    }
+
+    /** Text prefixed by this character will be considered tainted when doing recursive
+     * translations and won't be translated. */
+    protected static final String TAINT_CHAR = "~";
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressAggregator.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressAggregator.java
new file mode 100644 (file)
index 0000000..d74b011
--- /dev/null
@@ -0,0 +1,50 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+/**
+ * Accumulates the progress from a number of (potentially parallel) elements into a single smoothly
+ * progressing progress.
+ */
+public class ProgressAggregator
+{
+    public ProgressAggregator (ProgressObserver target, long[] sizes) {
+        _target = target;
+        _sizes = sizes;
+        _progress = new int[sizes.length];
+    }
+
+    public ProgressObserver startElement (final int index) {
+        return new ProgressObserver() {
+            public void progress (int percent) {
+                _progress[index] = percent;
+                updateAggProgress();
+            }
+        };
+    }
+
+    protected void updateAggProgress () {
+        long totalSize = 0L, currentSize = 0L;
+        synchronized (this) {
+            for (int ii = 0, ll = _sizes.length; ii < ll; ii++) {
+                long size = _sizes[ii];
+                totalSize += size;
+                currentSize += (int)((size * _progress[ii])/100.0);
+            }
+        }
+        _target.progress((int)(100.0*currentSize / totalSize));
+    }
+
+    protected static long sum (long[] sizes) {
+        long totalSize = 0L;
+        for (long size : sizes) totalSize += size;
+        return totalSize;
+    }
+
+    protected ProgressObserver _target;
+    protected long[] _sizes;
+    protected int[] _progress;
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressObserver.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressObserver.java
new file mode 100644 (file)
index 0000000..ad4c560
--- /dev/null
@@ -0,0 +1,18 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+/**
+ * Used to communicate progress.
+ */
+public interface ProgressObserver
+{
+    /**
+     * Informs the observer that we have completed the specified
+     * percentage of the process.
+     */
+    public void progress (int percent);
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Rectangle.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Rectangle.java
new file mode 100644 (file)
index 0000000..3671d7d
--- /dev/null
@@ -0,0 +1,40 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+/**
+ * An immutable rectangle.
+ */
+public class Rectangle
+{
+    public final int x;
+    public final int y;
+    public final int width;
+    public final int height;
+
+    public Rectangle (int x, int y, int width, int height)
+    {
+        this.x = x;
+        this.y = y;
+        this.width = width;
+        this.height = height;
+    }
+
+    public Rectangle union (Rectangle other) {
+        int x1 = Math.min(x, other.x);
+        int x2 = Math.max(x + width, other.x + other.width);
+        int y1 = Math.min(y, other.y);
+        int y2 = Math.max(y + height, other.y + other.height);
+        return new Rectangle(x1, y1, x2 - x1, y2 - y1);
+    }
+
+    /** {@inheritDoc} */
+    public String toString ()
+    {
+        return getClass().getName() + "[x=" + x + ", y=" + y +
+            ", width=" + width + ", height=" + height + "]";
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StreamUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StreamUtil.java
new file mode 100644 (file)
index 0000000..373cfff
--- /dev/null
@@ -0,0 +1,96 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.Charset;
+
+import static com.threerings.getdown.Log.log;
+
+public class StreamUtil {
+    /**
+     * Convenient close for a stream. Use in a finally clause and love life.
+     */
+    public static void close (InputStream in)
+    {
+        if (in != null) {
+            try {
+                in.close();
+            } catch (IOException ioe) {
+                log.warning("Error closing input stream", "stream", in, "cause", ioe);
+            }
+        }
+    }
+
+    /**
+     * Convenient close for a stream. Use in a finally clause and love life.
+     */
+    public static void close (OutputStream out)
+    {
+        if (out != null) {
+            try {
+                out.close();
+            } catch (IOException ioe) {
+                log.warning("Error closing output stream", "stream", out, "cause", ioe);
+            }
+        }
+    }
+
+    /**
+     * Convenient close for a Reader. Use in a finally clause and love life.
+     */
+    public static void close (Reader in)
+    {
+        if (in != null) {
+            try {
+                in.close();
+            } catch (IOException ioe) {
+                log.warning("Error closing reader", "reader", in, "cause", ioe);
+            }
+        }
+    }
+
+    /**
+     * Convenient close for a Writer. Use in a finally clause and love life.
+     */
+    public static void close (Writer out)
+    {
+        if (out != null) {
+            try {
+                out.close();
+            } catch (IOException ioe) {
+                log.warning("Error closing writer", "writer", out, "cause", ioe);
+            }
+        }
+    }
+
+    /**
+     * Copies the contents of the supplied input stream to the supplied output stream.
+     */
+    public static <T extends OutputStream> T copy (InputStream in, T out)
+        throws IOException
+    {
+        byte[] buffer = new byte[4096];
+        for (int read = 0; (read = in.read(buffer)) > 0; ) {
+            out.write(buffer, 0, read);
+        }
+        return out;
+    }
+
+    /**
+     * Reads the contents of the supplied stream into a byte array.
+     */
+    public static byte[] toByteArray (InputStream stream)
+        throws IOException
+    {
+        return copy(stream, new ByteArrayOutputStream()).toByteArray();
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StringUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StringUtil.java
new file mode 100644 (file)
index 0000000..03d3c9c
--- /dev/null
@@ -0,0 +1,206 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import java.util.StringTokenizer;
+
+public class StringUtil {
+
+    /**
+     * @return true if the specified string could be a valid URL (contains no illegal characters)
+     */
+    public static boolean couldBeValidUrl (String url)
+    {
+        return url.matches("[A-Za-z0-9\\-\\._~:/\\?#\\[\\]@!$&'\\(\\)\\*\\+,;=%]+");
+    }
+
+    /**
+     * @return true if the string is null or consists only of whitespace, false otherwise.
+     */
+    public static boolean isBlank (String value)
+    {
+        for (int ii = 0, ll = (value == null) ? 0 : value.length(); ii < ll; ii++) {
+            if (!Character.isWhitespace(value.charAt(ii))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Parses an array of integers from it's string representation. The array should be represented
+     * as a bare list of numbers separated by commas, for example:
+     *
+     * <pre>25, 17, 21, 99</pre>
+     *
+     * Any inability to parse the int array will result in the function returning null.
+     */
+    public static int[] parseIntArray (String source)
+    {
+        StringTokenizer tok = new StringTokenizer(source, ",");
+        int[] vals = new int[tok.countTokens()];
+        for (int i = 0; tok.hasMoreTokens(); i++) {
+            try {
+                // trim the whitespace from the token
+                vals[i] = Integer.parseInt(tok.nextToken().trim());
+            } catch (NumberFormatException nfe) {
+                return null;
+            }
+        }
+        return vals;
+    }
+
+    /**
+     * Parses an array of strings from a single string. The array should be represented as a bare
+     * list of strings separated by commas, for example:
+     *
+     * <pre>mary, had, a, little, lamb, and, an, escaped, comma,,</pre>
+     *
+     * If a comma is desired in one of the strings, it should be escaped by putting two commas in a
+     * row. Any inability to parse the string array will result in the function returning null.
+     */
+    public static String[] parseStringArray (String source)
+    {
+        return parseStringArray(source, false);
+    }
+
+    /**
+     * Like {@link #parseStringArray(String)} but can be instructed to invoke {@link String#intern}
+     * on the strings being parsed into the array.
+     */
+    public static String[] parseStringArray (String source, boolean intern)
+    {
+        int tcount = 0, tpos = -1, tstart = 0;
+
+        // empty strings result in zero length arrays
+        if (source.length() == 0) {
+            return new String[0];
+        }
+
+        // sort out escaped commas
+        source = source.replace(",,", "%COMMA%");
+
+        // count up the number of tokens
+        while ((tpos = source.indexOf(",", tpos+1)) != -1) {
+            tcount++;
+        }
+
+        String[] tokens = new String[tcount+1];
+        tpos = -1; tcount = 0;
+
+        // do the split
+        while ((tpos = source.indexOf(",", tpos+1)) != -1) {
+            tokens[tcount] = source.substring(tstart, tpos);
+            tokens[tcount] = tokens[tcount].trim().replace("%COMMA%", ",");
+            if (intern) {
+                tokens[tcount] = tokens[tcount].intern();
+            }
+            tstart = tpos+1;
+            tcount++;
+        }
+
+        // grab the last token
+        tokens[tcount] = source.substring(tstart);
+        tokens[tcount] = tokens[tcount].trim().replace("%COMMA%", ",");
+
+        return tokens;
+    }
+
+    /**
+     * @return the supplied string if it is non-null, "" if it is null.
+     */
+    public static String deNull (String value)
+    {
+        return (value == null) ? "" : value;
+    }
+
+    /**
+     * Generates a string from the supplied bytes that is the HEX encoded representation of those
+     * bytes.  Returns the empty string for a <code>null</code> or empty byte array.
+     *
+     * @param bytes the bytes for which we want a string representation.
+     * @param count the number of bytes to stop at (which will be coerced into being {@code <=} the
+     * length of the array).
+     */
+    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 < count; i++) {
+            int val = bytes[i];
+            if (val < 0) {
+                val += 256;
+            }
+            chars[2*i] = XLATE.charAt(val/16);
+            chars[2*i+1] = XLATE.charAt(val%16);
+        }
+
+        return new String(chars);
+    }
+
+    /**
+     * Generates a string from the supplied bytes that is the HEX encoded representation of those
+     * bytes.
+     */
+    public static String hexlate (byte[] bytes)
+    {
+        return (bytes == null) ? "" : hexlate(bytes, bytes.length);
+    }
+
+    /**
+     * Joins an array of strings (or objects which will be converted to strings) into a single
+     * string separated by commas.
+     */
+    public static String join (Object[] values)
+    {
+        return join(values, false);
+    }
+
+    /**
+     * Joins an array of strings into a single string, separated by commas, and optionally escaping
+     * commas that occur in the individual string values such that a subsequent call to {@link
+     * #parseStringArray} would recreate the string array properly. Any elements in the values
+     * array that are null will be treated as an empty string.
+     */
+    public static String join (Object[] values, boolean escape)
+    {
+        return join(values, ", ", escape);
+    }
+
+    /**
+     * Joins the supplied array of strings into a single string separated by the supplied
+     * separator.
+     */
+    public static String join (Object[] values, String separator)
+    {
+        return join(values, separator, false);
+    }
+
+    /**
+     * Helper function for the various <code>join</code> methods.
+     */
+    protected static String join (Object[] values, String separator, boolean escape)
+    {
+        StringBuilder buf = new StringBuilder();
+        int vlength = values.length;
+        for (int i = 0; i < vlength; i++) {
+            if (i > 0) {
+                buf.append(separator);
+            }
+            String value = (values[i] == null) ? "" : values[i].toString();
+            buf.append((escape) ? value.replace(",", ",,") : value);
+        }
+        return buf.toString();
+    }
+
+    /** Used by {@link #hexlate} and {@link #unhexlate}. */
+    protected static final String XLATE = "0123456789abcdef";
+}
diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/VersionUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/VersionUtil.java
new file mode 100644 (file)
index 0000000..49e4e6e
--- /dev/null
@@ -0,0 +1,114 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.threerings.getdown.data.SysProps;
+import static com.threerings.getdown.Log.log;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Version related utilities.
+ */
+public class VersionUtil
+{
+    /**
+     * Reads a version number from a file.
+     */
+    public static long readVersion (File vfile)
+    {
+        long fileVersion = -1;
+        try (BufferedReader bin =
+             new BufferedReader(new InputStreamReader(new FileInputStream(vfile), UTF_8))) {
+            String vstr = bin.readLine();
+            if (!StringUtil.isBlank(vstr)) {
+                fileVersion = Long.parseLong(vstr);
+            }
+        } catch (Exception e) {
+            log.info("Unable to read version file: " + e.getMessage());
+        }
+
+        return fileVersion;
+    }
+
+    /**
+     * Writes a version number to a file.
+     */
+    public static void writeVersion (File vfile, long version) throws IOException
+    {
+        try (PrintStream out = new PrintStream(new FileOutputStream(vfile))) {
+            out.println(version);
+        } catch (Exception e) {
+            log.warning("Unable to write version file: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Parses {@code versStr} using {@code versRegex} into a (long) integer version number.
+     * @see SysProps#parseJavaVersion
+     */
+    public static long parseJavaVersion (String versRegex, String versStr)
+    {
+        Matcher m = Pattern.compile(versRegex).matcher(versStr);
+        if (!m.matches()) return 0L;
+
+        long vers = 0L;
+        for (int ii = 1; ii <= m.groupCount(); ii++) {
+            String valstr = m.group(ii);
+            int value = (valstr == null) ? 0 : parseInt(valstr);
+            vers *= 100;
+            vers += value;
+        }
+        return vers;
+    }
+
+    /**
+     * Reads and parses the version from the {@code release} file bundled with a JVM.
+     */
+    public static long readReleaseVersion (File relfile, String versRegex)
+    {
+        try (BufferedReader in =
+             new BufferedReader(new InputStreamReader(new FileInputStream(relfile), UTF_8))) {
+            String line = null, relvers = null;
+            while ((line = in.readLine()) != null) {
+                if (line.startsWith("JAVA_VERSION=")) {
+                    relvers = line.substring("JAVA_VERSION=".length()).replace('"', ' ').trim();
+                }
+            }
+
+            if (relvers == null) {
+                log.warning("No JAVA_VERSION line in 'release' file", "file", relfile);
+                return 0L;
+            }
+            return parseJavaVersion(versRegex, relvers);
+
+        } catch (Exception e) {
+            log.warning("Failed to read version from 'release' file", "file", relfile, e);
+            return 0L;
+        }
+    }
+
+    private static int parseInt (String str) {
+        int value = 0;
+        for (int ii = 0, ll = str.length(); ii < ll; ii++) {
+            char c = str.charAt(ii);
+            if (c >= '0' && c <= '9') {
+                value *= 10;
+                value += (c - '0');
+            }
+        }
+        return value;
+    }
+}
diff --git a/getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java b/getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java
new file mode 100644 (file)
index 0000000..8af09da
--- /dev/null
@@ -0,0 +1,51 @@
+package jalview.bin;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+
+public class MemorySetting
+{
+  public static final long leaveFreeMinMemory = 536870912; // 0.5 GB
+
+  public static final long applicationMinMemory = 536870912; // 0.5 GB
+
+  protected static long getPhysicalMemory()
+  {
+    final OperatingSystemMXBean o = ManagementFactory
+            .getOperatingSystemMXBean();
+
+    try
+    {
+      if (o instanceof com.sun.management.OperatingSystemMXBean)
+      {
+        final com.sun.management.OperatingSystemMXBean osb = (com.sun.management.OperatingSystemMXBean) o;
+        return osb.getTotalPhysicalMemorySize();
+      }
+    } catch (NoClassDefFoundError e)
+    {
+      // com.sun.management.OperatingSystemMXBean doesn't exist in this JVM
+      System.err.println("No com.sun.management.OperatingSystemMXBean");
+    }
+
+    // We didn't get a com.sun.management.OperatingSystemMXBean.
+    return -1;
+  }
+
+  public static long memPercent(int percent)
+  {
+    long memPercent = -1;
+
+    long physicalMem = getPhysicalMemory();
+    if (physicalMem > applicationMinMemory)
+    {
+      // try and set at least applicationMinMemory and thereafter ensure
+      // leaveFreeMinMemory is left for the OS
+      memPercent = Math.max(applicationMinMemory,
+              physicalMem - Math.max(physicalMem * (100 - percent) / 100,
+                      leaveFreeMinMemory));
+    }
+
+    return memPercent;
+  }
+
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/GarbageCollectorTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/GarbageCollectorTest.java
new file mode 100644 (file)
index 0000000..d5a3937
--- /dev/null
@@ -0,0 +1,71 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.*;
+import org.junit.rules.TemporaryFolder;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.assumeTrue;
+
+/**
+ * Validates that cache garbage is collected and deleted correctly.
+ */
+public class GarbageCollectorTest
+{
+    @Before public void setupFiles () throws IOException
+    {
+        _cachedFile = _folder.newFile("abc123.jar");
+        _lastAccessedFile = _folder.newFile("abc123.jar" + ResourceCache.LAST_ACCESSED_FILE_SUFFIX);
+    }
+
+    @Test public void shouldDeleteCacheEntryIfRetentionPeriodIsReached ()
+    {
+        gcNow();
+        assertFalse(_cachedFile.exists());
+        assertFalse(_lastAccessedFile.exists());
+    }
+
+    @Test public void shouldDeleteCacheFolderIfFolderIsEmpty ()
+    {
+        gcNow();
+        assertFalse(_folder.getRoot().exists());
+    }
+
+    private void gcNow() {
+        GarbageCollector.collect(_folder.getRoot(), -1);
+    }
+
+    @Test public void shouldKeepFilesInCacheIfRententionPeriodIsNotReached ()
+    {
+        GarbageCollector.collect(_folder.getRoot(), TimeUnit.DAYS.toMillis(1));
+        assertTrue(_cachedFile.exists());
+        assertTrue(_lastAccessedFile.exists());
+    }
+
+    @Test public void shouldDeleteCachedFileIfLastAccessedFileIsMissing ()
+    {
+        assumeTrue(_lastAccessedFile.delete());
+        gcNow();
+        assertFalse(_cachedFile.exists());
+    }
+
+    @Test public void shouldDeleteLastAccessedFileIfCachedFileIsMissing ()
+    {
+        assumeTrue(_cachedFile.delete());
+        gcNow();
+        assertFalse(_lastAccessedFile.exists());
+    }
+
+    @Rule public TemporaryFolder _folder = new TemporaryFolder();
+
+    private File _cachedFile;
+    private File _lastAccessedFile;
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/ResourceCacheTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/ResourceCacheTest.java
new file mode 100644 (file)
index 0000000..860c72a
--- /dev/null
@@ -0,0 +1,72 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.*;
+import org.junit.rules.TemporaryFolder;
+
+import static org.junit.Assert.*;
+
+/**
+ * Asserts the correct functionality of the {@link ResourceCache}.
+ */
+public class ResourceCacheTest
+{
+    @Before public void setupCache () throws IOException {
+        _fileToCache = _folder.newFile("filetocache.jar");
+        _cache = new ResourceCache(_folder.newFolder(".cache"));
+    }
+
+    @Test public void shouldCacheFile () throws IOException
+    {
+        assertEquals("abc123.jar", cacheFile().getName());
+    }
+
+    private File cacheFile() throws IOException
+    {
+        return _cache.cacheFile(_fileToCache, "abc123", "abc123");
+    }
+
+    @Test public void shouldTrackFileUsage () throws IOException
+    {
+        String name = "abc123.jar" + ResourceCache.LAST_ACCESSED_FILE_SUFFIX;
+        File lastAccessedFile = new File(cacheFile().getParentFile(), name);
+        assertTrue(lastAccessedFile.exists());
+    }
+
+    @Test public void shouldNotCacheTheSameFile () throws Exception
+    {
+        File cachedFile = cacheFile();
+        cachedFile.setLastModified(YESTERDAY);
+        long expectedLastModified = cachedFile.lastModified();
+        // caching it another time
+        File sameCachedFile = cacheFile();
+        assertEquals(expectedLastModified, sameCachedFile.lastModified());
+    }
+
+    @Test public void shouldRememberWhenFileWasRequested () throws Exception
+    {
+        File cachedFile = cacheFile();
+        String name = cachedFile.getName() + ResourceCache.LAST_ACCESSED_FILE_SUFFIX;
+        File lastAccessedFile = new File(cachedFile.getParentFile(), name);
+        lastAccessedFile.setLastModified(YESTERDAY);
+        long lastAccessed = lastAccessedFile.lastModified();
+        // caching it another time
+        cacheFile();
+        assertTrue(lastAccessedFile.lastModified() > lastAccessed);
+    }
+
+    @Rule public TemporaryFolder _folder = new TemporaryFolder();
+
+    private File _fileToCache;
+    private ResourceCache _cache;
+
+    private static final long YESTERDAY = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1);
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/ClassPathTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/ClassPathTest.java
new file mode 100644 (file)
index 0000000..5344f3b
--- /dev/null
@@ -0,0 +1,54 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.LinkedHashSet;
+
+import org.junit.*;
+import org.junit.rules.TemporaryFolder;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for {@link ClassPath}.
+ */
+public class ClassPathTest
+{
+    @Before public void createJarsAndSetupClassPath () throws IOException
+    {
+        _firstJar = _folder.newFile("a.jar");
+        _secondJar = _folder.newFile("b.jar");
+
+        LinkedHashSet<File> classPathEntries = new LinkedHashSet<File>();
+        classPathEntries.add(_firstJar);
+        classPathEntries.add(_secondJar);
+        _classPath = new ClassPath(classPathEntries);
+    }
+
+    @Test public void shouldCreateValidArgumentString ()
+    {
+        assertEquals(
+            _firstJar.getAbsolutePath() + File.pathSeparator + _secondJar.getAbsolutePath(),
+            _classPath.asArgumentString());
+    }
+
+    @Test public void shouldProvideJarUrls () throws MalformedURLException, URISyntaxException
+    {
+        URL[] actualUrls = _classPath.asUrls();
+        assertEquals(_firstJar, new File(actualUrls[0].toURI()));
+        assertEquals(_secondJar, new File(actualUrls[1].toURI()));
+    }
+
+    @Rule public TemporaryFolder _folder = new TemporaryFolder();
+
+    private File _firstJar, _secondJar;
+    private ClassPath _classPath;
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/EnvConfigTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/EnvConfigTest.java
new file mode 100644 (file)
index 0000000..6178651
--- /dev/null
@@ -0,0 +1,142 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.io.File;
+
+import org.junit.*;
+import static org.junit.Assert.*;
+
+public class EnvConfigTest {
+
+    static String CWD = System.getProperty("user.dir");
+    static String TESTID = "testid";
+    static String TESTBASE = "https://test.com/test";
+
+    private void debugNotes(List<EnvConfig.Note> notes) {
+        for (EnvConfig.Note note : notes) {
+            System.out.println(note.message);
+        }
+    }
+
+    private void checkNoNotes (List<EnvConfig.Note> notes) {
+        StringBuilder msg = new StringBuilder();
+        for (EnvConfig.Note note : notes) {
+            if (note.level != EnvConfig.Note.Level.INFO) {
+                msg.append("\n").append(note.message);
+            }
+        }
+        if (msg.length() > 0) {
+            fail("Unexpected notes:" + msg.toString());
+        }
+    }
+    private void checkDir (EnvConfig cfg) {
+        assertTrue(cfg != null);
+        assertEquals(new File(CWD), cfg.appDir);
+    }
+    private void checkNoAppId (EnvConfig cfg) {
+        assertNull(cfg.appId);
+    }
+    private void checkAppId (EnvConfig cfg, String appId) {
+        assertEquals(appId, cfg.appId);
+    }
+    private void checkAppBase (EnvConfig cfg, String appBase) {
+        assertEquals(appBase, cfg.appBase);
+    }
+    private void checkNoAppBase (EnvConfig cfg) {
+        assertNull(cfg.appBase);
+    }
+    private void checkNoAppArgs (EnvConfig cfg) {
+        assertTrue(cfg.appArgs.isEmpty());
+    }
+    private void checkAppArgs (EnvConfig cfg, String... args) {
+        assertEquals(Arrays.asList(args), cfg.appArgs);
+    }
+
+    @Test public void testArgvDir () {
+        List<EnvConfig.Note> notes = new ArrayList<>();
+        String[] args = { CWD };
+        EnvConfig cfg = EnvConfig.create(args, notes);
+        // debugNotes(notes);
+        checkNoNotes(notes);
+        checkDir(cfg);
+        checkNoAppId(cfg);
+        checkNoAppBase(cfg);
+        checkNoAppArgs(cfg);
+    }
+
+    @Test public void testArgvDirId () {
+        List<EnvConfig.Note> notes = new ArrayList<>();
+        String[] args = { CWD, TESTID };
+        EnvConfig cfg = EnvConfig.create(args, notes);
+        // debugNotes(notes);
+        checkNoNotes(notes);
+        checkDir(cfg);
+        checkAppId(cfg, TESTID);
+        checkNoAppBase(cfg);
+        checkNoAppArgs(cfg);
+    }
+
+    @Test public void testArgvDirArgs () {
+        List<EnvConfig.Note> notes = new ArrayList<>();
+        String[] args = { CWD, "", "one", "two" };
+        EnvConfig cfg = EnvConfig.create(args, notes);
+        // debugNotes(notes);
+        checkNoNotes(notes);
+        checkDir(cfg);
+        checkNoAppId(cfg);
+        checkNoAppBase(cfg);
+        checkAppArgs(cfg, "one", "two");
+    }
+
+    @Test public void testArgvDirIdArgs () {
+        List<EnvConfig.Note> notes = new ArrayList<>();
+        String[] args = { CWD, TESTID, "one", "two" };
+        EnvConfig cfg = EnvConfig.create(args, notes);
+        // debugNotes(notes);
+        checkNoNotes(notes);
+        checkDir(cfg);
+        checkAppId(cfg, TESTID);
+        checkNoAppBase(cfg);
+        checkAppArgs(cfg, "one", "two");
+    }
+
+    private EnvConfig sysPropsConfig (List<EnvConfig.Note> notes, String... keyVals) {
+        for (int ii = 0; ii < keyVals.length; ii += 2) {
+            System.setProperty(keyVals[ii], keyVals[ii+1]);
+        }
+        EnvConfig cfg = EnvConfig.create(new String[0], notes);
+        for (int ii = 0; ii < keyVals.length; ii += 2) {
+            System.clearProperty(keyVals[ii]);
+        }
+        return cfg;
+    }
+
+    @Test public void testSysPropsDir () {
+        List<EnvConfig.Note> notes = new ArrayList<>();
+        EnvConfig cfg = sysPropsConfig(notes, "appdir", CWD);
+        // debugNotes(notes);
+        checkNoNotes(notes);
+        checkDir(cfg);
+        checkNoAppId(cfg);
+        checkNoAppBase(cfg);
+        checkNoAppArgs(cfg);
+    }
+
+    @Test public void testSysPropsDirIdBase () {
+        List<EnvConfig.Note> notes = new ArrayList<>();
+        EnvConfig cfg = sysPropsConfig(notes, "appdir", CWD, "appid", TESTID, "appbase", TESTBASE);
+        // debugNotes(notes);
+        checkNoNotes(notes);
+        checkDir(cfg);
+        checkAppId(cfg, TESTID);
+        checkAppBase(cfg, TESTBASE);
+        checkNoAppArgs(cfg);
+    }
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/PathBuilderTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/PathBuilderTest.java
new file mode 100644 (file)
index 0000000..7f35094
--- /dev/null
@@ -0,0 +1,70 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Arrays;
+
+import org.junit.*;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PathBuilderTest
+{
+    @Before public void setupFilesAndResources () throws IOException
+    {
+        _firstJarFile = _appdir.newFile("a.jar");
+        _secondJarFile = _appdir.newFile("b.jar");
+
+        when(_firstJar.getFinalTarget()).thenReturn(_firstJarFile);
+        when(_secondJar.getFinalTarget()).thenReturn(_secondJarFile);
+        when(_application.getActiveCodeResources()).thenReturn(Arrays.asList(_firstJar, _secondJar));
+        when(_application.getAppDir()).thenReturn(_appdir.getRoot());
+    }
+
+    @Test public void shouldBuildDefaultClassPath () throws IOException
+    {
+        ClassPath classPath = PathBuilder.buildDefaultClassPath(_application);
+        String expectedClassPath = _firstJarFile.getAbsolutePath() + File.pathSeparator +
+            _secondJarFile.getAbsolutePath();
+        assertEquals(expectedClassPath, classPath.asArgumentString());
+    }
+
+    @Test public void shouldBuildCachedClassPath () throws IOException
+    {
+        when(_application.getDigest(_firstJar)).thenReturn("first");
+        when(_application.getDigest(_secondJar)).thenReturn("second");
+        when(_application.getCodeCacheRetentionDays()).thenReturn(1);
+
+        Path firstCachedJarFile = _appdir.getRoot().toPath().
+            resolve(PathBuilder.CODE_CACHE_DIR).resolve("fi").resolve("first.jar");
+
+        Path secondCachedJarFile = _appdir.getRoot().toPath().
+            resolve(PathBuilder.CODE_CACHE_DIR).resolve("se").resolve("second.jar");
+
+        String expectedClassPath = firstCachedJarFile.toAbsolutePath() + File.pathSeparator +
+            secondCachedJarFile.toAbsolutePath();
+
+        ClassPath classPath = PathBuilder.buildCachedClassPath(_application);
+        assertEquals(expectedClassPath, classPath.asArgumentString());
+    }
+
+    @Mock protected Application _application;
+    @Mock protected Resource _firstJar;
+    @Mock protected Resource _secondJar;
+
+    protected File _firstJarFile, _secondJarFile;
+
+    @Rule public TemporaryFolder _appdir = new TemporaryFolder();
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/SysPropsTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/SysPropsTest.java
new file mode 100644 (file)
index 0000000..042a13f
--- /dev/null
@@ -0,0 +1,63 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.data;
+
+import org.junit.*;
+import static org.junit.Assert.*;
+
+public class SysPropsTest {
+
+    @After public void clearProps () {
+        System.clearProperty("delay");
+        System.clearProperty("appbase_domain");
+        System.clearProperty("appbase_override");
+    }
+
+    private static final String[] APPBASES = {
+        "http://foobar.com/myapp",
+        "https://foobar.com/myapp",
+        "http://foobar.com:8080/myapp",
+        "https://foobar.com:8080/myapp"
+    };
+
+    @Test public void testStartDelay () {
+
+        assertEquals(0, SysProps.startDelay());
+
+        System.setProperty("delay", "x");
+        assertEquals(0, SysProps.startDelay());
+
+        System.setProperty("delay", "-7");
+        assertEquals(0, SysProps.startDelay());
+
+        System.setProperty("delay", "7");
+        assertEquals(7, SysProps.startDelay());
+
+        System.setProperty("delay", "1440");
+        assertEquals(1440, SysProps.startDelay());
+
+        System.setProperty("delay", "1441");
+        assertEquals(1440, SysProps.startDelay());
+    }
+
+    @Test public void testAppbaseDomain () {
+        System.setProperty("appbase_domain", "https://barbaz.com");
+        for (String appbase : APPBASES) {
+            assertEquals("https://barbaz.com/myapp", SysProps.overrideAppbase(appbase));
+        }
+        System.setProperty("appbase_domain", "http://barbaz.com");
+        for (String appbase : APPBASES) {
+            assertEquals("http://barbaz.com/myapp", SysProps.overrideAppbase(appbase));
+        }
+    }
+
+    @Test public void testAppbaseOverride () {
+        System.setProperty("appbase_override", "https://barbaz.com/newapp");
+        for (String appbase : APPBASES) {
+            assertEquals("https://barbaz.com/newapp", SysProps.overrideAppbase(appbase));
+        }
+    }
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/ColorTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/ColorTest.java
new file mode 100644 (file)
index 0000000..7aa48ee
--- /dev/null
@@ -0,0 +1,23 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link Color}.
+ */
+public class ColorTest
+{
+    @Test
+    public void testBrightness() {
+        assertEquals(0, Color.brightness(0xFF000000), 0.0000001);
+        assertEquals(1, Color.brightness(0xFFFFFFFF), 0.0000001);
+        assertEquals(0.0117647, Color.brightness(0xFF010203), 0.0000001);
+        assertEquals(1, Color.brightness(0xFF00FFC8), 0.0000001);
+    }
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/ConfigTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/ConfigTest.java
new file mode 100644 (file)
index 0000000..cdc5a91
--- /dev/null
@@ -0,0 +1,171 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.List;
+import java.util.Random;
+
+import org.junit.*;
+import static org.junit.Assert.*;
+
+/**
+ * Tests {@link Config}.
+ */
+public class ConfigTest
+{
+    public static class Pair {
+        public final String key;
+        public final String value;
+        public Pair (String key, String value) {
+            this.key = key;
+            this.value = value;
+        }
+    }
+
+    public static final Pair[] SIMPLE_PAIRS = {
+        new Pair("one", "two"),
+        new Pair("three", "four"),
+        new Pair("five", "six"),
+        new Pair("seven", "eight"),
+        new Pair("nine", "ten"),
+    };
+
+    @Test public void testSimplePairs () throws IOException
+    {
+        List<String[]> pairs = Config.parsePairs(
+            toReader(SIMPLE_PAIRS), Config.createOpts(true));
+        for (int ii = 0; ii < SIMPLE_PAIRS.length; ii++) {
+            assertEquals(SIMPLE_PAIRS[ii].key, pairs.get(ii)[0]);
+            assertEquals(SIMPLE_PAIRS[ii].value, pairs.get(ii)[1]);
+        }
+    }
+
+    @Test public void testQualifiedPairs () throws IOException
+    {
+        Pair linux = new Pair("one", "[linux] two");
+        Pair mac = new Pair("three", "[mac os x] four");
+        Pair linuxAndMac = new Pair("five", "[linux, mac os x] six");
+        Pair linux64 = new Pair("seven", "[linux-x86_64] eight");
+        Pair linux64s = new Pair("nine", "[linux-x86_64, linux-amd64] ten");
+        Pair mac64 = new Pair("eleven", "[mac os x-x86_64] twelve");
+        Pair win64 = new Pair("thirteen", "[windows-x86_64] fourteen");
+        Pair notWin = new Pair("fifteen", "[!windows] sixteen");
+        Pair[] pairs = { linux, mac, linuxAndMac, linux64, linux64s, mac64, win64, notWin };
+
+        Config.ParseOpts opts = Config.createOpts(false);
+        opts.osname = "linux";
+        opts.osarch = "i386";
+        List<String[]> parsed = Config.parsePairs(toReader(pairs), opts);
+        assertTrue(exists(parsed, linux.key));
+        assertTrue(!exists(parsed, mac.key));
+        assertTrue(exists(parsed, linuxAndMac.key));
+        assertTrue(!exists(parsed, linux64.key));
+        assertTrue(!exists(parsed, linux64s.key));
+        assertTrue(!exists(parsed, mac64.key));
+        assertTrue(!exists(parsed, win64.key));
+        assertTrue(exists(parsed, notWin.key));
+
+        opts.osarch = "x86_64";
+        parsed = Config.parsePairs(toReader(pairs), opts);
+        assertTrue(exists(parsed, linux.key));
+        assertTrue(!exists(parsed, mac.key));
+        assertTrue(exists(parsed, linuxAndMac.key));
+        assertTrue(exists(parsed, linux64.key));
+        assertTrue(exists(parsed, linux64s.key));
+        assertTrue(!exists(parsed, mac64.key));
+        assertTrue(!exists(parsed, win64.key));
+        assertTrue(exists(parsed, notWin.key));
+
+        opts.osarch = "amd64";
+        parsed = Config.parsePairs(toReader(pairs), opts);
+        assertTrue(exists(parsed, linux.key));
+        assertTrue(!exists(parsed, mac.key));
+        assertTrue(exists(parsed, linuxAndMac.key));
+        assertTrue(!exists(parsed, linux64.key));
+        assertTrue(exists(parsed, linux64s.key));
+        assertTrue(!exists(parsed, mac64.key));
+        assertTrue(!exists(parsed, win64.key));
+        assertTrue(exists(parsed, notWin.key));
+
+        opts.osname = "mac os x";
+        opts.osarch = "x86_64";
+        parsed = Config.parsePairs(toReader(pairs), opts);
+        assertTrue(!exists(parsed, linux.key));
+        assertTrue(exists(parsed, mac.key));
+        assertTrue(exists(parsed, linuxAndMac.key));
+        assertTrue(!exists(parsed, linux64.key));
+        assertTrue(!exists(parsed, linux64s.key));
+        assertTrue(exists(parsed, mac64.key));
+        assertTrue(!exists(parsed, win64.key));
+        assertTrue(exists(parsed, notWin.key));
+
+        opts.osname = "windows";
+        opts.osarch = "i386";
+        parsed = Config.parsePairs(toReader(pairs), opts);
+        assertTrue(!exists(parsed, linux.key));
+        assertTrue(!exists(parsed, mac.key));
+        assertTrue(!exists(parsed, linuxAndMac.key));
+        assertTrue(!exists(parsed, linux64.key));
+        assertTrue(!exists(parsed, linux64s.key));
+        assertTrue(!exists(parsed, mac64.key));
+        assertTrue(!exists(parsed, win64.key));
+        assertTrue(!exists(parsed, notWin.key));
+
+        opts.osarch = "x86_64";
+        parsed = Config.parsePairs(toReader(pairs), opts);
+        assertTrue(!exists(parsed, linux.key));
+        assertTrue(!exists(parsed, mac.key));
+        assertTrue(!exists(parsed, linuxAndMac.key));
+        assertTrue(!exists(parsed, linux64.key));
+        assertTrue(!exists(parsed, linux64s.key));
+        assertTrue(!exists(parsed, mac64.key));
+        assertTrue(exists(parsed, win64.key));
+        assertTrue(!exists(parsed, notWin.key));
+
+        opts.osarch = "amd64";
+        parsed = Config.parsePairs(toReader(pairs), opts);
+        assertTrue(!exists(parsed, linux.key));
+        assertTrue(!exists(parsed, mac.key));
+        assertTrue(!exists(parsed, linuxAndMac.key));
+        assertTrue(!exists(parsed, linux64.key));
+        assertTrue(!exists(parsed, linux64s.key));
+        assertTrue(!exists(parsed, mac64.key));
+        assertTrue(!exists(parsed, win64.key));
+        assertTrue(!exists(parsed, notWin.key));
+    }
+
+    protected static boolean exists (List<String[]> pairs, String key)
+    {
+        for (String[] pair : pairs) {
+            if (pair[0].equals(key)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    protected static StringReader toReader (Pair[] pairs)
+    {
+        StringBuilder builder = new StringBuilder();
+        for (Pair pair : pairs) {
+            // throw some whitespace in to ensure it's trimmed
+            builder.append(whitespace()).append(pair.key).
+                append(whitespace()).append("=").
+                append(whitespace()).append(pair.value).
+                append(whitespace()).append("\n");
+        }
+        return new StringReader(builder.toString());
+    }
+
+    protected static String whitespace ()
+    {
+        return _rando.nextBoolean() ? " " : "";
+    }
+
+    protected static Random _rando = new Random();
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/FileUtilTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/FileUtilTest.java
new file mode 100644 (file)
index 0000000..cfd53a2
--- /dev/null
@@ -0,0 +1,60 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.List;
+
+import org.junit.*;
+import org.junit.rules.TemporaryFolder;
+
+import static org.junit.Assert.*;
+
+/**
+ * Tests {@link FileUtil}.
+ */
+public class FileUtilTest
+{
+    @Test public void testReadLines () throws IOException
+    {
+        String data = "This is a test\nof a file with\na few lines\n";
+        List<String> lines = FileUtil.readLines(new StringReader(data));
+        String[] linesBySplit = data.split("\n");
+        assertEquals(linesBySplit.length, lines.size());
+        for (int ii = 0; ii < lines.size(); ii++) {
+            assertEquals(linesBySplit[ii], lines.get(ii));
+        }
+    }
+
+    @Test public void shouldCopyFile () throws IOException
+    {
+        File source = _folder.newFile("source.txt");
+        File target = new File(_folder.getRoot(), "target.txt");
+        assertFalse(target.exists());
+        FileUtil.copy(source, target);
+        assertTrue(target.exists());
+    }
+
+    @Test public void shouldRecursivelyWalkOverFilesAndFolders () throws IOException
+    {
+        _folder.newFile("a.txt");
+        new File(_folder.newFolder("b"), "b.txt").createNewFile();
+
+        class CountingVisitor implements FileUtil.Visitor {
+            int fileCount = 0;
+            @Override public void visit(File file) {
+                fileCount++;
+            }
+        }
+        CountingVisitor visitor = new CountingVisitor();
+        FileUtil.walkTree(_folder.getRoot(), visitor);
+        assertEquals(3, visitor.fileCount);
+    }
+
+    @Rule public TemporaryFolder _folder = new TemporaryFolder();
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/HostWhitelistTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/HostWhitelistTest.java
new file mode 100644 (file)
index 0000000..703afef
--- /dev/null
@@ -0,0 +1,159 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link HostWhitelist}.
+ */
+public class HostWhitelistTest
+{
+    @Test
+    public void testVerify () throws MalformedURLException
+    {
+        checkCanVerify("foo.com", "http://foo.com", true);
+        checkCanVerify("foo.com", "http://foo.com/", true);
+        checkCanVerify("foo.com", "http://foo.com/x/y/z", true);
+        checkCanVerify("foo.com", "http://www.foo.com", false);
+        checkCanVerify("foo.com", "http://www.foo.com/", false);
+        checkCanVerify("foo.com", "http://www.foo.com/x/y/z", false);
+        checkCanVerify("foo.com", "http://a.b.foo.com", false);
+        checkCanVerify("foo.com", "http://a.b.foo.com/", false);
+        checkCanVerify("foo.com", "http://a.b.foo.com/x/y/z", false);
+        checkCanVerify("foo.com", "http://oo.com", false);
+        checkCanVerify("foo.com", "http://f.oo.com", false);
+        checkCanVerify("foo.com", "http://a.f.oo.com", false);
+
+        checkCanVerify("*.foo.com", "http://foo.com", false);
+        checkCanVerify("*.foo.com", "http://foo.com/", false);
+        checkCanVerify("*.foo.com", "http://foo.com/x/y/z", false);
+        checkCanVerify("*.foo.com", "http://www.foo.com", true);
+        checkCanVerify("*.foo.com", "http://www.foo.com/", true);
+        checkCanVerify("*.foo.com", "http://www.foo.com/x/y/z", true);
+        checkCanVerify("*.foo.com", "http://a.b.foo.com", true);
+        checkCanVerify("*.foo.com", "http://a.b.foo.com/", true);
+        checkCanVerify("*.foo.com", "http://a.b.foo.com/x/y/z", true);
+        checkCanVerify("*.foo.com", "http://oo.com", false);
+        checkCanVerify("*.foo.com", "http://f.oo.com", false);
+        checkCanVerify("*.foo.com", "http://a.f.oo.com", false);
+
+        checkCanVerify("*.com", "http://foo.com", true);
+        checkCanVerify("*.com", "http://foo.com/", true);
+        checkCanVerify("*.com", "http://foo.com/x/y/z", true);
+        checkCanVerify("*.com", "http://www.foo.com", true);
+        checkCanVerify("*.com", "http://www.foo.com/", true);
+        checkCanVerify("*.com", "http://www.foo.com/x/y/z", true);
+        checkCanVerify("*.com", "http://a.b.foo.com", true);
+        checkCanVerify("*.com", "http://a.b.foo.com/", true);
+        checkCanVerify("*.com", "http://a.b.foo.com/x/y/z", true);
+        checkCanVerify("*.com", "http://oo.com", true);
+        checkCanVerify("*.com", "http://f.oo.com", true);
+        checkCanVerify("*.com", "http://a.f.oo.com", true);
+
+        checkCanVerify("*.net", "http://foo.com", false);
+        checkCanVerify("*.net", "http://foo.com/", false);
+        checkCanVerify("*.net", "http://foo.com/x/y/z", false);
+        checkCanVerify("*.net", "http://www.foo.com", false);
+        checkCanVerify("*.net", "http://www.foo.com/", false);
+        checkCanVerify("*.net", "http://www.foo.com/x/y/z", false);
+        checkCanVerify("*.net", "http://a.b.foo.com", false);
+        checkCanVerify("*.net", "http://a.b.foo.com/", false);
+        checkCanVerify("*.net", "http://a.b.foo.com/x/y/z", false);
+        checkCanVerify("*.net", "http://oo.com", false);
+        checkCanVerify("*.net", "http://f.oo.com", false);
+        checkCanVerify("*.net", "http://a.f.oo.com", false);
+
+        checkCanVerify("www.*.com", "http://foo.com", false);
+        checkCanVerify("www.*.com", "http://foo.com/", false);
+        checkCanVerify("www.*.com", "http://foo.com/x/y/z", false);
+        checkCanVerify("www.*.com", "http://www.foo.com", true);
+        checkCanVerify("www.*.com", "http://www.foo.com/", true);
+        checkCanVerify("www.*.com", "http://www.foo.com/x/y/z", true);
+        checkCanVerify("www.*.com", "http://a.b.foo.com", false);
+        checkCanVerify("www.*.com", "http://a.b.foo.com/", false);
+        checkCanVerify("www.*.com", "http://a.b.foo.com/x/y/z", false);
+        checkCanVerify("www.*.com", "http://oo.com", false);
+        checkCanVerify("www.*.com", "http://f.oo.com", false);
+        checkCanVerify("www.*.com", "http://a.f.oo.com", false);
+        checkCanVerify("www.*.com", "http://www.a.f.oo.com", true);
+
+        checkCanVerify("foo.*", "http://foo.com", true);
+        checkCanVerify("foo.*", "http://foo.com/", true);
+        checkCanVerify("foo.*", "http://foo.com/x/y/z", true);
+        checkCanVerify("foo.*", "http://www.foo.com", false);
+        checkCanVerify("foo.*", "http://www.foo.com/", false);
+        checkCanVerify("foo.*", "http://www.foo.com/x/y/z", false);
+        checkCanVerify("foo.*", "http://a.b.foo.com", false);
+        checkCanVerify("foo.*", "http://a.b.foo.com/", false);
+        checkCanVerify("foo.*", "http://a.b.foo.com/x/y/z", false);
+        checkCanVerify("foo.*", "http://oo.com", false);
+        checkCanVerify("foo.*", "http://f.oo.com", false);
+        checkCanVerify("foo.*", "http://a.f.oo.com", false);
+
+        checkCanVerify("*.foo.*", "http://foo.com", false);
+        checkCanVerify("*.foo.*", "http://foo.com/", false);
+        checkCanVerify("*.foo.*", "http://foo.com/x/y/z", false);
+        checkCanVerify("*.foo.*", "http://www.foo.com", true);
+        checkCanVerify("*.foo.*", "http://www.foo.com/", true);
+        checkCanVerify("*.foo.*", "http://www.foo.com/x/y/z", true);
+        checkCanVerify("*.foo.*", "http://a.b.foo.com", true);
+        checkCanVerify("*.foo.*", "http://a.b.foo.com/", true);
+        checkCanVerify("*.foo.*", "http://a.b.foo.com/x/y/z", true);
+        checkCanVerify("*.foo.*", "http://oo.com", false);
+        checkCanVerify("*.foo.*", "http://f.oo.com", false);
+        checkCanVerify("*.foo.*", "http://a.f.oo.com", false);
+
+        checkCanVerify("127.0.0.1", "http://127.0.0.1", true);
+        checkCanVerify("127.0.0.1", "http://127.0.0.1/", true);
+        checkCanVerify("127.0.0.1", "http://127.0.0.1/x/y/z", true);
+        checkCanVerify("*.0.0.1", "http://127.0.0.1/abc", true);
+        checkCanVerify("127.*.0.1", "http://127.0.0.1/abc", true);
+        checkCanVerify("127.0.*.1", "http://127.0.0.1/abc", true);
+        checkCanVerify("127.0.0.*", "http://127.0.0.1/abc", true);
+        checkCanVerify("127.*.1", "http://127.0.0.1/abc", true);
+        checkCanVerify("*.0.1", "http://127.0.0.1/abc", true);
+        checkCanVerify("127.0.*", "http://127.0.0.1/abc", true);
+        checkCanVerify("*", "http://127.0.0.1/abc", true);
+        checkCanVerify("127.0.0.2", "http://127.0.0.1", false);
+        checkCanVerify("127.0.2.1", "http://127.0.0.1", false);
+        checkCanVerify("127.2.0.1", "http://127.0.0.1", false);
+        checkCanVerify("222.0.0.1", "http://127.0.0.1", false);
+
+        checkCanVerify("", "http://foo.com", true);
+        checkCanVerify("", "http://aaa.bbb.net/xyz", true);
+        checkCanVerify("", "https://127.0.0.1/abc", true);
+
+        checkCanVerify("aaa.bbb.com,xxx.yyy.com, *.jjj.net", "http://aaa.bbb.com/m", true);
+        checkCanVerify("aaa.bbb.com, xxx.yyy.com,*.jjj.net", "http://xxx.yyy.com/n", true);
+        checkCanVerify("aaa.bbb.com,xxx.yyy.com, *.jjj.net", "http://www.jjj.net/o", true);
+    }
+
+    private static void checkCanVerify (String whitelist, String url, boolean expectedToPass)
+        throws MalformedURLException
+    {
+        List<String> w = Arrays.asList(StringUtil.parseStringArray(whitelist));
+        URL u = new URL(url);
+        boolean passed;
+
+        try {
+            HostWhitelist.verify(w, u);
+            passed = true;
+        } catch (MalformedURLException e) {
+            passed = false;
+        }
+
+        assertEquals("with whitelist '" + whitelist + "' and URL '" + url + "'",
+            expectedToPass, passed);
+    }
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/StringUtilTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/StringUtilTest.java
new file mode 100644 (file)
index 0000000..f70bab9
--- /dev/null
@@ -0,0 +1,28 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import org.junit.Test;
+
+import static com.threerings.getdown.util.StringUtil.couldBeValidUrl;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests {@link StringUtil}.
+ */
+public class StringUtilTest
+{
+    @Test public void testCouldBeValidUrl ()
+    {
+        assertTrue(couldBeValidUrl("http://www.foo.com/"));
+        assertTrue(couldBeValidUrl("http://www.foo.com/A-B-C/1_2_3/~bar/q.jsp?x=u+i&y=2;3;4#baz%20baz"));
+        assertTrue(couldBeValidUrl("https://user:secret@www.foo.com/"));
+
+        assertFalse(couldBeValidUrl("http://www.foo.com & echo hello"));
+        assertFalse(couldBeValidUrl("http://www.foo.com\""));
+    }
+}
diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/VersionUtilTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/util/VersionUtilTest.java
new file mode 100644 (file)
index 0000000..165fbe3
--- /dev/null
@@ -0,0 +1,53 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class VersionUtilTest {
+
+    @Test
+    public void shouldParseJavaVersion ()
+    {
+        long version = VersionUtil.parseJavaVersion(
+            "(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(_\\d+)?)?)?", "1.8.0_152");
+        assertEquals(1_080_152, version);
+    }
+
+    @Test
+    public void shouldParseJavaVersion8 ()
+    {
+        long version = VersionUtil.parseJavaVersion(
+            "(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(_\\d+)?)?)?", "1.8");
+        assertEquals(1_080_000, version);
+    }
+
+    @Test
+    public void shouldParseJavaVersion9 ()
+    {
+        long version = VersionUtil.parseJavaVersion(
+            "(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(_\\d+)?)?)?", "9");
+        assertEquals(9_000_000, version);
+    }
+
+    @Test
+    public void shouldParseJavaVersion10 ()
+    {
+        long version = VersionUtil.parseJavaVersion(
+            "(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)(_\\d+)?)?)?", "10");
+        assertEquals(10_000_000, version);
+    }
+
+    @Test
+    public void shouldParseJavaRuntimeVersion ()
+    {
+        long version = VersionUtil.parseJavaVersion(
+            "(\\d+)\\.(\\d+)\\.(\\d+)(_\\d+)?(-b\\d+)?", "1.8.0_131-b11");
+        assertEquals(108_013_111, version);
+    }
+}
diff --git a/getdown/src/getdown/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/getdown/src/getdown/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644 (file)
index 0000000..1f0955d
--- /dev/null
@@ -0,0 +1 @@
+mock-maker-inline
diff --git a/getdown/src/getdown/launcher/.project-MOVED b/getdown/src/getdown/launcher/.project-MOVED
new file mode 100644 (file)
index 0000000..d77a6e8
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>getdown-launcher</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.m2e.core.maven2Builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+               <nature>org.eclipse.m2e.core.maven2Nature</nature>
+       </natures>
+</projectDescription>
diff --git a/getdown/src/getdown/launcher/.settings/org.eclipse.core.resources.prefs b/getdown/src/getdown/launcher/.settings/org.eclipse.core.resources.prefs
new file mode 100644 (file)
index 0000000..abdea9a
--- /dev/null
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding/<project>=UTF-8
diff --git a/getdown/src/getdown/launcher/.settings/org.eclipse.jdt.core.prefs b/getdown/src/getdown/launcher/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..54e5672
--- /dev/null
@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/getdown/src/getdown/launcher/.settings/org.eclipse.m2e.core.prefs b/getdown/src/getdown/launcher/.settings/org.eclipse.m2e.core.prefs
new file mode 100644 (file)
index 0000000..f897a7f
--- /dev/null
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/getdown/src/getdown/launcher/pom.xml b/getdown/src/getdown/launcher/pom.xml
new file mode 100644 (file)
index 0000000..bf0b736
--- /dev/null
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>com.threerings.getdown</groupId>
+    <artifactId>getdown</artifactId>
+    <version>1.8.3-1.1.8_FJVL</version>
+  </parent>
+
+  <artifactId>getdown-launcher</artifactId>
+  <packaging>jar</packaging>
+  <name>Getdown Launcher</name>
+  <description>The Getdown app updater/launcher</description>
+
+  <repositories>
+    <repository>
+      <id>lib-repo</id>
+      <url>file://${basedir}/../lib</url>
+    </repository>
+  </repositories>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.threerings.getdown</groupId>
+      <artifactId>getdown-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.samskivert</groupId>
+      <artifactId>samskivert</artifactId>
+      <version>1.2</version>
+    </dependency>
+    <dependency>
+      <groupId>jregistrykey</groupId>
+      <artifactId>jregistrykey</artifactId>
+      <version>1.0</version>
+      <optional>true</optional>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <!--
+      <plugin>
+        <groupId>com.github.wvengen</groupId>
+        <artifactId>proguard-maven-plugin</artifactId>
+        <version>2.0.14</version>
+        <executions>
+         <execution>
+           <phase>package</phase>
+           <goals>
+             <goal>proguard</goal>
+           </goals>
+         </execution>
+        </executions>
+        <dependencies>
+          <dependency>
+            <groupId>net.sf.proguard</groupId>
+            <artifactId>proguard-base</artifactId>
+            <version>6.0.3</version>
+            <scope>runtime</scope>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <proguardVersion>6.0.3</proguardVersion>
+          <outputDirectory>${project.build.directory}</outputDirectory>
+          <outjar>${project.build.finalName}.jar</outjar>
+          <injar>${project.build.finalName}.jar</injar>
+          <assembly>
+            <inclusions>
+              <inclusion>
+                <groupId>com.threerings.getdown</groupId>
+                <artifactId>getdown-core</artifactId>
+              </inclusion>
+              <inclusion>
+                <groupId>com.samskivert</groupId>
+                <artifactId>samskivert</artifactId>
+                <filter>
+                  !**/*.java,
+                  !**/swing/RuntimeAdjust*,
+                  !**/swing/util/ButtonUtil*,
+                  !**/util/CalendarUtil*,
+                  !**/util/Calendars*,
+                  !**/util/Log4JLogger*,
+                  !**/util/PrefsConfig*,
+                  !**/util/SignalUtil*,
+                  com/samskivert/Log.class,
+                  **/samskivert/io/**,
+                  **/samskivert/swing/**,
+                  **/samskivert/text/**,
+                  **/samskivert/util/**
+                </filter>
+              </inclusion>
+              <inclusion>
+                <groupId>jregistrykey</groupId>
+                <artifactId>jregistrykey</artifactId>
+              </inclusion>
+            </inclusions>
+          </assembly>
+          <obfuscate>true</obfuscate>
+          <options>
+            <option>-keep public class com.threerings.getdown.** { *; }</option>
+            <option>-keep public class ca.beq.util.win32.registry.** { *; }</option>
+            <option>-keepattributes Exceptions, InnerClasses, Signature</option>
+          </options>
+          <libs>
+            <lib>${rt.jar.path}</lib>
+          </libs>
+          <addMavenDescriptor>false</addMavenDescriptor>
+        </configuration>
+      </plugin>
+      -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>3.1.0</version>
+        <configuration>
+          <archive>
+            <manifest>
+              <mainClass>com.threerings.getdown.launcher.GetdownApp</mainClass>
+            </manifest>
+            <manifestEntries>
+              <Permissions>all-permissions</Permissions>
+              <Application-Name>Getdown</Application-Name>
+              <Codebase>*</Codebase>
+              <Application-Library-Allowable-Codebase>*</Application-Library-Allowable-Codebase>
+              <Caller-Allowable-Codebase>*</Caller-Allowable-Codebase>
+              <Trusted-Library>true</Trusted-Library>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>3.2.1</version>
+        <configuration>
+          <!-- put your configurations here -->
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <!--
+            <configuration>
+              <minimizeJar>true</minimizeJar>
+              <filters>
+                <filter>
+                  <artifact>install4j-runtime</artifact>
+                  <includes>
+                    <include>**</include>
+                  </includes>
+                </filter>
+              </filters>
+            </configuration>
+            -->
+          </execution>
+        </executions>
+      </plugin>
+
+
+
+
+    </plugins>
+  </build>
+
+  <profiles>
+    <!-- finagling to find rt.jar -->
+    <profile>
+      <id>non-mac-jre</id>
+      <activation>
+        <file><exists>${java.home}/../lib/rt.jar</exists></file>
+      </activation>
+      <properties>
+        <rt.jar.path>${java.home}/../lib/rt.jar</rt.jar.path>
+      </properties>
+    </profile>
+    <profile>
+      <id>non-mac-jdk</id>
+      <activation>
+        <file><exists>${java.home}/lib/rt.jar</exists></file>
+      </activation>
+      <properties>
+        <rt.jar.path>${java.home}/lib/rt.jar</rt.jar.path>
+      </properties>
+    </profile>
+    <profile>
+      <id>java-9-jdk</id>
+      <activation>
+        <file><exists>${java.home}/jmods/java.base.jmod</exists></file>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>com.github.wvengen</groupId>
+            <artifactId>proguard-maven-plugin</artifactId>
+            <configuration>
+              <libs>
+                <lib>${java.home}/jmods/java.base.jmod</lib>
+                <lib>${java.home}/jmods/java.desktop.jmod</lib>
+                <lib>${java.home}/jmods/java.logging.jmod</lib>
+                <lib>${java.home}/jmods/jdk.jsobject.jmod</lib>
+              </libs>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/AbortPanel.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/AbortPanel.java
new file mode 100644 (file)
index 0000000..dc1e54e
--- /dev/null
@@ -0,0 +1,100 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.launcher;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import com.samskivert.swing.GroupLayout;
+import com.samskivert.swing.Spacer;
+import com.samskivert.swing.VGroupLayout;
+
+import com.threerings.getdown.util.MessageUtil;
+import static com.threerings.getdown.Log.log;
+
+/**
+ * Displays a confirmation that the user wants to abort installation.
+ */
+public final class AbortPanel extends JFrame
+    implements ActionListener
+{
+    public AbortPanel (Getdown getdown, ResourceBundle msgs)
+    {
+        _getdown = getdown;
+        _msgs = msgs;
+
+        setLayout(new VGroupLayout());
+        setResizable(false);
+        setTitle(get("m.abort_title"));
+
+        JLabel message = new JLabel(get("m.abort_confirm"));
+        message.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+        add(message);
+        add(new Spacer(5, 5));
+
+        JPanel row = GroupLayout.makeButtonBox(GroupLayout.CENTER);
+        JButton button;
+        row.add(button = new JButton(get("m.abort_ok")));
+        button.setActionCommand("ok");
+        button.addActionListener(this);
+        row.add(button = new JButton(get("m.abort_cancel")));
+        button.setActionCommand("cancel");
+        button.addActionListener(this);
+        getRootPane().setDefaultButton(button);
+        add(row);
+    }
+
+    // documentation inherited
+    @Override
+    public Dimension getPreferredSize ()
+    {
+        // this is annoyingly hardcoded, but we can't just force the width
+        // or the JLabel will claim a bogus height thinking it can lay its
+        // text out all on one line which will booch the whole UI's
+        // preferred size
+        return new Dimension(300, 200);
+    }
+
+    // documentation inherited from interface
+    public void actionPerformed (ActionEvent e)
+    {
+        String cmd = e.getActionCommand();
+        if (cmd.equals("ok")) {
+            System.exit(0);
+        } else {
+            setVisible(false);
+        }
+    }
+
+    /** Used to look up localized messages. */
+    protected String get (String key)
+    {
+        // if this string is tainted, we don't translate it, instead we
+        // simply remove the taint character and return it to the caller
+        if (MessageUtil.isTainted(key)) {
+            return MessageUtil.untaint(key);
+        }
+        try {
+            return _msgs.getString(key);
+        } catch (MissingResourceException mre) {
+            log.warning("Missing translation message '" + key + "'.");
+            return key;
+        }
+    }
+
+    protected Getdown _getdown;
+    protected ResourceBundle _msgs;
+}
diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java
new file mode 100644 (file)
index 0000000..01bf370
--- /dev/null
@@ -0,0 +1,1182 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.launcher;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Graphics;
+import java.awt.GraphicsEnvironment;
+import java.awt.Image;
+import java.awt.event.ActionEvent;
+import java.awt.image.BufferedImage;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.*;
+
+import javax.imageio.ImageIO;
+import javax.swing.AbstractAction;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLayeredPane;
+import javax.swing.JPanel;
+
+import com.samskivert.swing.util.SwingUtil;
+import com.threerings.getdown.data.*;
+import com.threerings.getdown.data.Application.UpdateInterface.Step;
+import com.threerings.getdown.net.Downloader;
+import com.threerings.getdown.net.HTTPDownloader;
+import com.threerings.getdown.tools.Patcher;
+import com.threerings.getdown.util.*;
+
+import static com.threerings.getdown.Log.log;
+
+/**
+ * Manages the main control for the Getdown application updater and deployment system.
+ */
+public abstract class Getdown extends Thread
+    implements Application.StatusDisplay, RotatingBackgrounds.ImageLoader
+{
+    public Getdown (EnvConfig envc)
+    {
+        super("Getdown");
+        try {
+            // If the silent property exists, install without bringing up any gui. If it equals
+            // launch, start the application after installing. Otherwise, just install and exit.
+            _silent = SysProps.silent();
+            if (_silent) {
+                _launchInSilent = SysProps.launchInSilent();
+                _noUpdate = SysProps.noUpdate();
+            }
+            // If we're running in a headless environment and have not otherwise customized
+            // silence, operate without a UI and do launch the app.
+            if (!_silent && GraphicsEnvironment.isHeadless()) {
+                log.info("Running in headless JVM, will attempt to operate without UI.");
+                _silent = true;
+                _launchInSilent = true;
+            }
+            _delay = SysProps.startDelay();
+        } catch (SecurityException se) {
+            // don't freak out, just assume non-silent and no delay; we're probably already
+            // recovering from a security failure
+        }
+        try {
+            _msgs = ResourceBundle.getBundle("com.threerings.getdown.messages");
+        } catch (Exception e) {
+            // welcome to hell, where java can't cope with a classpath that contains jars that live
+            // in a directory that contains a !, at least the same bug happens on all platforms
+            String dir = envc.appDir.toString();
+            if (dir.equals(".")) {
+                dir = System.getProperty("user.dir");
+            }
+            String errmsg = "The directory in which this application is installed:\n" + dir +
+                "\nis invalid (" + e.getMessage() + "). If the full path to the app directory " +
+                "contains the '!' character, this will trigger this error.";
+            fail(errmsg);
+        }
+        _app = new Application(envc);
+        _startup = System.currentTimeMillis();
+    }
+
+    /**
+     * Returns true if there are pending new resources, waiting to be installed.
+     */
+    public boolean isUpdateAvailable ()
+    {
+        return _readyToInstall && !_toInstallResources.isEmpty();
+    }
+
+    /**
+     * Installs the currently pending new resources.
+     */
+    public void install () throws IOException
+    {
+        if (SysProps.noInstall()) {
+            log.info("Skipping install due to 'no_install' sysprop.");
+        } else if (_readyToInstall) {
+            log.info("Installing " + _toInstallResources.size() + " downloaded resources:");
+            for (Resource resource : _toInstallResources) {
+                resource.install(true);
+            }
+            _toInstallResources.clear();
+            _readyToInstall = false;
+            log.info("Install completed.");
+        } else {
+            log.info("Nothing to install.");
+        }
+    }
+
+    @Override
+    public void run ()
+    {
+        // if we have no messages, just bail because we're hosed; the error message will be
+        // displayed to the user already
+        if (_msgs == null) {
+            return;
+        }
+
+        log.info("Getdown starting", "version", Build.version(), "built", Build.time());
+
+        // determine whether or not we can write to our install directory
+        File instdir = _app.getLocalPath("");
+        if (!instdir.canWrite()) {
+            String path = instdir.getPath();
+            if (path.equals(".")) {
+                path = System.getProperty("user.dir");
+            }
+            fail(MessageUtil.tcompose("m.readonly_error", path));
+            return;
+        }
+
+        try {
+            _dead = false;
+            // if we fail to detect a proxy, but we're allowed to run offline, then go ahead and
+            // run the app anyway because we're prepared to cope with not being able to update
+            if (detectProxy() || _app.allowOffline()) {
+                getdown();
+            } else if (_silent) {
+                log.warning("Need a proxy, but we don't want to bother anyone.  Exiting.");
+            } else {
+                // create a panel they can use to configure the proxy settings
+                _container = createContainer();
+                // allow them to close the window to abort the proxy configuration
+                _dead = true;
+                configureContainer();
+                ProxyPanel panel = new ProxyPanel(this, _msgs);
+                // set up any existing configured proxy
+                String[] hostPort = ProxyUtil.loadProxy(_app);
+                panel.setProxy(hostPort[0], hostPort[1]);
+                _container.add(panel, BorderLayout.CENTER);
+                showContainer();
+            }
+
+        } catch (Exception e) {
+            log.warning("run() failed.", e);
+            String msg = e.getMessage();
+            if (msg == null) {
+                msg = MessageUtil.compose("m.unknown_error", _ifc.installError);
+            } else if (!msg.startsWith("m.")) {
+                // try to do something sensible based on the type of error
+                if (e instanceof FileNotFoundException) {
+                    msg = MessageUtil.compose(
+                        "m.missing_resource", MessageUtil.taint(msg), _ifc.installError);
+                } else {
+                    msg = MessageUtil.compose(
+                        "m.init_error", MessageUtil.taint(msg), _ifc.installError);
+                }
+            }
+            fail(msg);
+        }
+    }
+
+    /**
+     * Configures our proxy settings (called by {@link ProxyPanel}) and fires up the launcher.
+     */
+    public void configProxy (String host, String port, String username, String password)
+    {
+        log.info("User configured proxy", "host", host, "port", port);
+
+        if (!StringUtil.isBlank(host)) {
+            ProxyUtil.configProxy(_app, host, port, username, password);
+        }
+
+        // clear out our UI
+        disposeContainer();
+        _container = null;
+
+        // fire up a new thread
+        new Thread(this).start();
+    }
+
+    protected boolean detectProxy () {
+        if (ProxyUtil.autoDetectProxy(_app)) {
+            return true;
+        }
+
+        // otherwise see if we actually need a proxy; first we have to initialize our application
+        // to get some sort of interface configuration and the appbase URL
+        log.info("Checking whether we need to use a proxy...");
+        try {
+            readConfig(true);
+        } catch (IOException ioe) {
+            // no worries
+        }
+        updateStatus("m.detecting_proxy");
+        if (!ProxyUtil.canLoadWithoutProxy(_app.getConfigResource().getRemote())) {
+            return false;
+        }
+
+        // we got through, so we appear not to require a proxy; make a blank proxy config so that
+        // we don't go through this whole detection process again next time
+        log.info("No proxy appears to be needed.");
+        ProxyUtil.saveProxy(_app, null, null);
+        return true;
+    }
+
+    protected void readConfig (boolean preloads) throws IOException {
+        Config config = _app.init(true);
+        if (preloads) doPredownloads(_app.getResources());
+        _ifc = new Application.UpdateInterface(config);
+        if (_status != null) {
+          _status.setAppbase(_app.getAppbase());
+        }
+    }
+
+    /**
+     * Downloads and installs (without verifying) any resources that are marked with a
+     * {@code PRELOAD} attribute.
+     * @param resources the full set of resources from the application (the predownloads will be
+     * extracted from it).
+     */
+    protected void doPredownloads (Collection<Resource> resources) {
+        List<Resource> predownloads = new ArrayList<>();
+        for (Resource rsrc : resources) {
+            if (rsrc.shouldPredownload() && !rsrc.getLocal().exists()) {
+                predownloads.add(rsrc);
+            }
+        }
+
+        try {
+            download(predownloads);
+            for (Resource rsrc : predownloads) {
+                rsrc.install(false); // install but don't validate yet
+            }
+        } catch (IOException ioe) {
+            log.warning("Failed to predownload resources. Continuing...", ioe);
+        }
+    }
+
+    /**
+     * Does the actual application validation, update and launching business.
+     */
+    protected void getdown ()
+    {
+        try {
+            // first parses our application deployment file
+            try {
+                readConfig(true);
+            } catch (IOException ioe) {
+                log.warning("Failed to initialize: " + ioe);
+                _app.attemptRecovery(this);
+                // and re-initalize
+                readConfig(true);
+                // and force our UI to be recreated with the updated info
+                createInterfaceAsync(true);
+            }
+            if (!_noUpdate && !_app.lockForUpdates()) {
+                throw new MultipleGetdownRunning();
+            }
+
+            // Update the config modtime so a sleeping getdown will notice the change.
+            File config = _app.getLocalPath(Application.CONFIG_FILE);
+            if (!config.setLastModified(System.currentTimeMillis())) {
+                log.warning("Unable to set modtime on config file, will be unable to check for " +
+                            "another instance of getdown running while this one waits.");
+            }
+            if (_delay > 0) {
+                // don't hold the lock while waiting, let another getdown proceed if it starts.
+                _app.releaseLock();
+                // Store the config modtime before waiting the delay amount of time
+                long lastConfigModtime = config.lastModified();
+                log.info("Waiting " + _delay + " minutes before beginning actual work.");
+                Thread.sleep(_delay * 60 * 1000);
+                if (lastConfigModtime < config.lastModified()) {
+                    log.warning("getdown.txt was modified while getdown was waiting.");
+                    throw new MultipleGetdownRunning();
+                }
+            }
+
+            // if no_update was specified, directly start the app without updating
+            if (_noUpdate) {
+                log.info("Launching without update!");
+                launch();
+                return;
+            }
+
+            // we create this tracking counter here so that we properly note the first time through
+            // the update process whether we previously had validated resources (which means this
+            // is not a first time install); we may, in the course of updating, wipe out our
+            // validation markers and revalidate which would make us think we were doing a fresh
+            // install if we didn't specifically remember that we had validated resources the first
+            // time through
+            int[] alreadyValid = new int[1];
+
+            // we'll keep track of all the resources we unpack
+            Set<Resource> unpacked = new HashSet<>();
+
+            _toInstallResources = new HashSet<>();
+            _readyToInstall = false;
+
+            // setStep(Step.START);
+            for (int ii = 0; ii < MAX_LOOPS; ii++) {
+                // make sure we have the desired version and that the metadata files are valid...
+                setStep(Step.VERIFY_METADATA);
+                setStatusAsync("m.validating", -1, -1L, false);
+                if (_app.verifyMetadata(this)) {
+                    log.info("Application requires update.");
+                    update();
+                    // loop back again and reverify the metadata
+                    continue;
+                }
+
+                // now verify (and download) our resources...
+                setStep(Step.VERIFY_RESOURCES);
+                setStatusAsync("m.validating", -1, -1L, false);
+                Set<Resource> toDownload = new HashSet<>();
+                _app.verifyResources(_progobs, alreadyValid, unpacked,
+                                     _toInstallResources, toDownload);
+
+                if (toDownload.size() > 0) {
+                    // we have resources to download, also note them as to-be-installed
+                    for (Resource r : toDownload) {
+                        if (!_toInstallResources.contains(r)) {
+                            _toInstallResources.add(r);
+                        }
+                    }
+
+                    try {
+                        // if any of our resources have already been marked valid this is not a
+                        // first time install and we don't want to enable tracking
+                        _enableTracking = (alreadyValid[0] == 0);
+                        reportTrackingEvent("app_start", -1);
+
+                        // redownload any that are corrupt or invalid...
+                        log.info(toDownload.size() + " of " + _app.getAllActiveResources().size() +
+                                 " rsrcs require update (" + alreadyValid[0] + " assumed valid).");
+                        setStep(Step.REDOWNLOAD_RESOURCES);
+                        download(toDownload);
+
+                        reportTrackingEvent("app_complete", -1);
+
+                    } finally {
+                        _enableTracking = false;
+                    }
+
+                    // now we'll loop back and try it all again
+                    continue;
+                }
+
+                // if we aren't running in a JVM that meets our version requirements, either
+                // complain or attempt to download and install the appropriate version
+                if (!_app.haveValidJavaVersion()) {
+                    // download and install the necessary version of java, then loop back again and
+                    // reverify everything; if we can't download java; we'll throw an exception
+                    log.info("Attempting to update Java VM...");
+                    setStep(Step.UPDATE_JAVA);
+                    _enableTracking = true; // always track JVM downloads
+                    try {
+                        updateJava();
+                    } finally {
+                        _enableTracking = false;
+                    }
+                    continue;
+                }
+
+                // if we were downloaded in full from another service (say, Steam), we may
+                // not have unpacked all of our resources yet
+                if (Boolean.getBoolean("check_unpacked")) {
+                    File ufile = _app.getLocalPath("unpacked.dat");
+                    long version = -1;
+                    long aversion = _app.getVersion();
+                    if (!ufile.exists()) {
+                        ufile.createNewFile();
+                    } else {
+                        version = VersionUtil.readVersion(ufile);
+                    }
+
+                    if (version < aversion) {
+                        log.info("Performing unpack", "version", version, "aversion", aversion);
+                        setStep(Step.UNPACK);
+                        updateStatus("m.validating");
+                        _app.unpackResources(_progobs, unpacked);
+                        try {
+                            VersionUtil.writeVersion(ufile, aversion);
+                        } catch (IOException ioe) {
+                            log.warning("Failed to update unpacked version", ioe);
+                        }
+                    }
+                }
+
+                // assuming we're not doing anything funny, install the update
+                _readyToInstall = true;
+                install();
+
+                // Only launch if we aren't in silent mode. Some mystery program starting out
+                // of the blue would be disconcerting.
+                if (!_silent || _launchInSilent) {
+                    // And another final check for the lock. It'll already be held unless
+                    // we're in silent mode.
+                    _app.lockForUpdates();
+                    launch();
+                }
+                return;
+            }
+
+            log.warning("Pants! We couldn't get the job done.");
+            throw new IOException("m.unable_to_repair");
+
+        } catch (Exception e) {
+            log.warning("getdown() failed.", e);
+            String msg = e.getMessage();
+            if (msg == null) {
+                msg = MessageUtil.compose("m.unknown_error", _ifc.installError);
+            } else if (!msg.startsWith("m.")) {
+                // try to do something sensible based on the type of error
+                if (e instanceof FileNotFoundException) {
+                    msg = MessageUtil.compose(
+                        "m.missing_resource", MessageUtil.taint(msg), _ifc.installError);
+                } else {
+                    msg = MessageUtil.compose(
+                        "m.init_error", MessageUtil.taint(msg), _ifc.installError);
+                }
+            }
+            // Since we're dead, clear off the 'time remaining' label along with displaying the
+            // error message
+            fail(msg);
+            _app.releaseLock();
+        }
+    }
+
+    // documentation inherited from interface
+    @Override
+    public void updateStatus (String message)
+    {
+        setStatusAsync(message, -1, -1L, true);
+    }
+
+    /**
+     * Load the image at the path. Before trying the exact path/file specified we will look to see
+     * if we can find a localized version by sticking a {@code _<language>} in front of the "." in
+     * the filename.
+     */
+    @Override
+    public BufferedImage loadImage (String path)
+    {
+        if (StringUtil.isBlank(path)) {
+            return null;
+        }
+
+        File imgpath = null;
+        try {
+            // First try for a localized image.
+            String localeStr = Locale.getDefault().getLanguage();
+            imgpath = _app.getLocalPath(path.replace(".", "_" + localeStr + "."));
+            return ImageIO.read(imgpath);
+        } catch (IOException ioe) {
+            // No biggie, we'll try the generic one.
+        }
+
+        // If that didn't work, try a generic one.
+        try {
+            imgpath = _app.getLocalPath(path);
+            return ImageIO.read(imgpath);
+        } catch (IOException ioe2) {
+            log.warning("Failed to load image", "path", imgpath, "error", ioe2);
+            return null;
+        }
+    }
+
+    /**
+     * Downloads and installs an Java VM bundled with the application. This is called if we are not
+     * running with the necessary Java version.
+     */
+    protected void updateJava ()
+        throws IOException
+    {
+        Resource vmjar = _app.getJavaVMResource();
+        if (vmjar == null) {
+            throw new IOException("m.java_download_failed");
+        }
+
+        // on Windows, if the local JVM is in use, we will not be able to replace it with an
+        // updated JVM; we detect this by attempting to rename the java.dll to its same name, which
+        // will fail on Windows for in use files; hackery!
+        File javaLocalDir = new File(_app.getAppDir(), LaunchUtil.LOCAL_JAVA_DIR+File.separator);
+        File javaDll = new File(javaLocalDir, "bin" + File.separator + "java.dll");
+        if (javaDll.exists()) {
+            if (!javaDll.renameTo(javaDll)) {
+                log.info("Cannot update local Java VM as it is in use.");
+                return;
+            }
+            log.info("Can update local Java VM as it is not in use.");
+        }
+
+        reportTrackingEvent("jvm_start", -1);
+
+        updateStatus("m.downloading_java");
+        List<Resource> list = new ArrayList<>();
+        list.add(vmjar);
+        download(list);
+
+        reportTrackingEvent("jvm_unpack", -1);
+
+        updateStatus("m.unpacking_java");
+        vmjar.install(true);
+
+        // these only run on non-Windows platforms, so we use Unix file separators
+        String localJavaDir = LaunchUtil.LOCAL_JAVA_DIR + "/";
+        FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "bin/java"));
+        FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "lib/jspawnhelper"));
+        FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "lib/amd64/jspawnhelper"));
+
+        // lastly regenerate the .jsa dump file that helps Java to start up faster
+        String vmpath = LaunchUtil.getJVMPath(_app.getLocalPath(""));
+        try {
+            log.info("Regenerating classes.jsa for " + vmpath + "...");
+            Runtime.getRuntime().exec(vmpath + " -Xshare:dump");
+        } catch (Exception e) {
+            log.warning("Failed to regenerate .jsa dump file", "error", e);
+        }
+
+        reportTrackingEvent("jvm_complete", -1);
+    }
+
+    /**
+     * Called if the application is determined to be of an old version.
+     */
+    protected void update ()
+        throws IOException
+    {
+        // first clear all validation markers
+        _app.clearValidationMarkers();
+
+        // attempt to download the patch files
+        Resource patch = _app.getPatchResource(null);
+        if (patch != null) {
+            List<Resource> list = new ArrayList<>();
+            list.add(patch);
+
+            // add the auxiliary group patch files for activated groups
+            for (Application.AuxGroup aux : _app.getAuxGroups()) {
+                if (_app.isAuxGroupActive(aux.name)) {
+                    patch = _app.getPatchResource(aux.name);
+                    if (patch != null) {
+                        list.add(patch);
+                    }
+                }
+            }
+
+            // show the patch notes button, if applicable
+            if (!StringUtil.isBlank(_ifc.patchNotesUrl)) {
+                createInterfaceAsync(false);
+                EQinvoke(new Runnable() {
+                    public void run () {
+                        _patchNotes.setVisible(true);
+                    }
+                });
+            }
+
+            // download the patch files...
+            setStep(Step.DOWNLOAD);
+            download(list);
+
+            // and apply them...
+            setStep(Step.PATCH);
+            updateStatus("m.patching");
+
+            long[] sizes = new long[list.size()];
+            Arrays.fill(sizes, 1L);
+            ProgressAggregator pragg = new ProgressAggregator(_progobs, sizes);
+            int ii = 0; for (Resource prsrc : list) {
+                ProgressObserver pobs = pragg.startElement(ii++);
+                try {
+                    // install the patch file (renaming them from _new)
+                    prsrc.install(false);
+                    // now apply the patch
+                    Patcher patcher = new Patcher();
+                    patcher.patch(prsrc.getLocal().getParentFile(), prsrc.getLocal(), pobs);
+                } catch (Exception e) {
+                    log.warning("Failed to apply patch", "prsrc", prsrc, e);
+                }
+
+                // clean up the patch file
+                if (!FileUtil.deleteHarder(prsrc.getLocal())) {
+                    log.warning("Failed to delete '" + prsrc + "'.");
+                }
+            }
+        }
+
+        // if the patch resource is null, that means something was booched in the application, so
+        // we skip the patching process but update the metadata which will result in a "brute
+        // force" upgrade
+
+        // finally update our metadata files...
+        _app.updateMetadata();
+        // ...and reinitialize the application
+        readConfig(false);
+    }
+
+    /**
+     * Called if the application is determined to require resource downloads.
+     */
+    protected void download (Collection<Resource> resources)
+        throws IOException
+    {
+        // create our user interface
+        createInterfaceAsync(false);
+
+        Downloader dl = new HTTPDownloader(_app.proxy) {
+            @Override protected void resolvingDownloads () {
+                updateStatus("m.resolving");
+            }
+
+            @Override protected void downloadProgress (int percent, long remaining) {
+                // check for another getdown running at 0 and every 10% after that
+                if (_lastCheck == -1 || percent >= _lastCheck + 10) {
+                    if (_delay > 0) {
+                        // stop the presses if something else is holding the lock
+                        boolean locked = _app.lockForUpdates();
+                        _app.releaseLock();
+                        if (locked) abort();
+                    }
+                    _lastCheck = percent;
+                }
+                setStatusAsync("m.downloading", stepToGlobalPercent(percent), remaining, true);
+                if (percent > 0) {
+                    reportTrackingEvent("progress", percent);
+                }
+            }
+
+            @Override protected void downloadFailed (Resource rsrc, Exception e) {
+                updateStatus(MessageUtil.tcompose("m.failure", e.getMessage()));
+                log.warning("Download failed", "rsrc", rsrc, e);
+            }
+
+            /** The last percentage at which we checked for another getdown running, or -1 for not
+             * having checked at all. */
+            protected int _lastCheck = -1;
+        };
+        if (!dl.download(resources, _app.maxConcurrentDownloads())) {
+            // if we aborted due to detecting another getdown running, we want to report here
+            throw new MultipleGetdownRunning();
+        }
+    }
+
+    /**
+     * Called to launch the application if everything is determined to be ready to go.
+     */
+    protected void launch ()
+    {
+        setStep(Step.LAUNCH);
+        setStatusAsync("m.launching", stepToGlobalPercent(100), -1L, false);
+
+        try {
+            if (invokeDirect()) {
+                // we want to close the Getdown window, as the app is launching
+                disposeContainer();
+                _app.releaseLock();
+                _app.invokeDirect();
+
+            } else {
+                Process proc;
+                if (_app.hasOptimumJvmArgs()) {
+                    // if we have "optimum" arguments, we want to try launching with them first
+                    proc = _app.createProcess(true);
+
+                    long fallback = System.currentTimeMillis() + FALLBACK_CHECK_TIME;
+                    boolean error = false;
+                    while (fallback > System.currentTimeMillis()) {
+                        try {
+                            error = proc.exitValue() != 0;
+                            break;
+                        } catch (IllegalThreadStateException e) {
+                            Thread.yield();
+                        }
+                    }
+
+                    if (error) {
+                        log.info("Failed to launch with optimum arguments; falling back.");
+                        proc = _app.createProcess(false);
+                    }
+                } else {
+                    proc = _app.createProcess(false);
+                }
+                
+                // close standard in to avoid choking standard out of the launched process
+                proc.getInputStream().close();
+                // close standard out, since we're not going to write to anything to it anyway
+                proc.getOutputStream().close();
+
+                // on Windows 98 and ME we need to stick around and read the output of stderr lest
+                // the process fill its output buffer and choke, yay!
+                final InputStream stderr = proc.getErrorStream();
+                if (LaunchUtil.mustMonitorChildren()) {
+                    // close our window if it's around
+                    disposeContainer();
+                    _container = null;
+                    copyStream(stderr, System.err);
+                    log.info("Process exited: " + proc.waitFor());
+
+                } else {
+                    // spawn a daemon thread that will catch the early bits of stderr in case the
+                    // launch fails
+                    Thread t = new Thread() {
+                        @Override public void run () {
+                            copyStream(stderr, System.err);
+                        }
+                    };
+                    t.setDaemon(true);
+                    t.start();
+                }
+            }
+
+            //_container.setVisible(true);
+            //_container.validate();
+
+            // if we have a UI open and we haven't been around for at least 5 seconds (the default
+            // for min_show_seconds), don't stick a fork in ourselves straight away but give our
+            // lovely user a chance to see what we're doing
+            long uptime = System.currentTimeMillis() - _startup;
+            long minshow = _ifc.minShowSeconds * 1000L;
+            if (_container != null && uptime < minshow) {
+                try {
+                    Thread.sleep(minshow - uptime);
+                } catch (Exception e) {
+                }
+            }
+
+            // pump the percent up to 100%
+            setStatusAsync(null, 100, -1L, false);
+            exit(0);
+
+        } catch (Exception e) {
+            log.warning("launch() failed.", e);
+        }
+    }
+
+    /**
+     * Creates our user interface, which we avoid doing unless we actually have to update
+     * something. NOTE: this happens on the next UI tick, not immediately.
+     *
+     * @param reinit - if the interface should be reinitialized if it already exists.
+     */
+    protected void createInterfaceAsync (final boolean reinit)
+    {
+        if (_silent || (_container != null && !reinit)) {
+            return;
+        }
+        
+        EQinvoke (new Runnable() {
+            public void run () {
+               
+                if (_container == null || reinit) {
+                    if (_container == null) {
+                        _container = createContainer();
+                    } else {
+                        _container.removeAll();
+                    }
+                    configureContainer();
+                    _layers = new JLayeredPane();
+                    
+                    
+                    
+                    // added in the instant display of a splashscreen
+                    try {
+                      readConfig(false);
+                      Graphics g = _container.getGraphics();
+                      BufferedImage iBgImage = loadImage(_ifc.instantBackgroundImage);
+                      boolean ibg = true;
+                      if (iBgImage == null) {
+                         iBgImage = loadImage(_ifc.backgroundImage);
+                         ibg = false;
+                      }
+                      if (iBgImage != null) {
+                                                 final BufferedImage bgImage = iBgImage;
+                                                 int bwidth = bgImage.getWidth();
+                                                 int bheight = bgImage.getHeight();
+                                                 
+                                                 log.info("Displaying instant background image", ibg?"instant_background_image":"background_image");
+
+                                                 instantSplashPane = new JPanel() {
+                                                       @Override
+                                                       protected void paintComponent(Graphics g)
+                                                       {
+                                                         super.paintComponent(g);
+                                                         // attempt to draw a background image...
+                                                         if (bgImage != null) {
+                                                               g.drawImage(bgImage, 0, 0, this);
+                                                         }
+                                                       }
+                                                 };
+
+                                                 instantSplashPane.setSize(bwidth,bheight);
+                                                 instantSplashPane.setPreferredSize(new Dimension(bwidth,bheight));
+
+                                                 _layers.add(instantSplashPane, Integer.valueOf(1));
+                                                 
+                                                 _container.setPreferredSize(new Dimension(bwidth,bheight));
+                      }
+                    } catch (Exception e) {
+                      log.warning("Failed to set instant background image", "ibg", _ifc.instantBackgroundImage);
+                    }
+                    
+                    _container.add(_layers, BorderLayout.CENTER);
+                    _patchNotes = new JButton(new AbstractAction(_msgs.getString("m.patch_notes")) {
+                        @Override public void actionPerformed (ActionEvent event) {
+                            showDocument(_ifc.patchNotesUrl);
+                        }
+                    });
+                    _patchNotes.setFont(StatusPanel.FONT);
+                    _layers.add(_patchNotes);
+                    _status = new StatusPanel(_msgs);
+                    setStatusAsync("m.initialising", 0, -1L, true);
+                    //_layers.add(_status, Integer.valueOf(2));
+                    initInterface();
+                }
+                showContainer();
+            }
+        });
+        
+    }
+
+    /**
+     * Initializes the interface with the current UpdateInterface and backgrounds.
+     */
+    protected void initInterface ()
+    {
+        RotatingBackgrounds newBackgrounds = getBackground();
+        if (_background == null || newBackgrounds.getNumImages() > 0) {
+            // Leave the old _background in place if there is an old one to leave in place
+            // and the new getdown.txt didn't yield any images.
+            _background = newBackgrounds;
+        }
+        _status.init(_ifc, _background, getProgressImage());
+        Dimension size = _status.getPreferredSize();
+        _status.setSize(size);
+        //_status.updateStatusLabel();
+        _layers.setPreferredSize(size);
+
+        _patchNotes.setBounds(_ifc.patchNotes.x, _ifc.patchNotes.y,
+                              _ifc.patchNotes.width, _ifc.patchNotes.height);
+        _patchNotes.setVisible(false);
+
+        // we were displaying progress while the UI wasn't up. Now that it is, whatever progress
+        // is left is scaled into a 0-100 DISPLAYED progress.
+        _uiDisplayPercent = _lastGlobalPercent;
+        _stepMinPercent = _lastGlobalPercent = 0;
+    }
+
+    protected RotatingBackgrounds getBackground ()
+    {
+        if (_ifc.rotatingBackgrounds != null && _ifc.rotatingBackgrounds.size() > 0) {
+            if (_ifc.backgroundImage != null) {
+                log.warning("ui.background_image and ui.rotating_background were both specified. " +
+                            "The background image is being used.");
+            }
+            return new RotatingBackgrounds(_ifc.rotatingBackgrounds, _ifc.errorBackground, Getdown.this);
+        } else if (_ifc.backgroundImage != null) {
+            return new RotatingBackgrounds(loadImage(_ifc.backgroundImage));
+        } else {
+            return new RotatingBackgrounds();
+        }
+    }
+
+    protected Image getProgressImage ()
+    {
+        return loadImage(_ifc.progressImage);
+    }
+
+    protected void handleWindowClose ()
+    {
+        if (_dead) {
+            exit(0);
+        } else {
+            if (_abort == null) {
+                _abort = new AbortPanel(Getdown.this, _msgs);
+            }
+            _abort.pack();
+            SwingUtil.centerWindow(_abort);
+            _abort.setVisible(true);
+            _abort.setState(JFrame.NORMAL);
+            _abort.requestFocus();
+        }
+    }
+
+    /**
+     * Update the status to indicate getdown has failed for the reason in <code>message</code>.
+     */
+    protected void fail (String message)
+    {
+        _dead = true;
+        setStatusAsync(message, stepToGlobalPercent(0), -1L, true);
+    }
+
+    /**
+     * Set the current step, which will be used to globalize per-step percentages.
+     */
+    protected void setStep (Step step)
+    {
+        int finalPercent = -1;
+        for (Integer perc : _ifc.stepPercentages.get(step)) {
+            if (perc > _stepMaxPercent) {
+                finalPercent = perc;
+                break;
+            }
+        }
+        if (finalPercent == -1) {
+            // we've gone backwards and this step will be ignored
+            return;
+        }
+
+        _stepMaxPercent = finalPercent;
+        _stepMinPercent = _lastGlobalPercent;
+    }
+
+    /**
+     * Convert a step percentage to the global percentage.
+     */
+    protected int stepToGlobalPercent (int percent)
+    {
+        int adjustedMaxPercent =
+            ((_stepMaxPercent - _uiDisplayPercent) * 100) / (100 - _uiDisplayPercent);
+        _lastGlobalPercent = Math.max(_lastGlobalPercent,
+            _stepMinPercent + (percent * (adjustedMaxPercent - _stepMinPercent)) / 100);
+        return _lastGlobalPercent;
+    }
+
+    /**
+     * Updates the status. NOTE: this happens on the next UI tick, not immediately.
+     */
+    protected void setStatusAsync (final String message, final int percent, final long remaining,
+                                   boolean createUI)
+    {
+        if (_status == null && createUI) {
+            createInterfaceAsync(false);
+        }
+
+        EQinvoke(new Runnable() {
+            public void run () {
+
+               if (_status == null) {
+                    if (message != null) {
+                        log.info("Dropping status '" + message + "'.");
+                    }
+                    return;
+                }
+                if (message != null) {
+                    _status.setStatus(message, _dead);
+                }
+                if (_dead) {
+                    _status.setProgress(0, -1L);
+                } else if (percent >= 0) {
+                    _status.setProgress(percent, remaining);
+                } else {
+                       //_status.setStatus("m.initialising", false);
+                }
+                
+            }
+        });
+        
+        if (_status != null && ! _addedStatusLayer) {
+               _layers.add(_status, Integer.valueOf(2));
+               _addedStatusLayer = true;
+               initInterface();
+        }
+        
+    }
+
+    protected void reportTrackingEvent (String event, int progress)
+    {
+        if (!_enableTracking) {
+            return;
+
+        } else if (progress > 0) {
+            // we need to make sure we do the right thing if we skip over progress levels
+            do {
+                URL url = _app.getTrackingProgressURL(++_reportedProgress);
+                if (url != null) {
+                    new ProgressReporter(url).start();
+                }
+            } while (_reportedProgress <= progress);
+
+        } else {
+            URL url = _app.getTrackingURL(event);
+            if (url != null) {
+                new ProgressReporter(url).start();
+            }
+        }
+    }
+
+    /**
+     * Creates the container in which our user interface will be displayed.
+     */
+    protected abstract Container createContainer ();
+
+    /**
+     * Configures the interface container based on the latest UI config.
+     */
+    protected abstract void configureContainer ();
+
+    /**
+     * Shows the container in which our user interface will be displayed.
+     */
+    protected abstract void showContainer ();
+
+    /**
+     * Disposes the container in which we have our user interface.
+     */
+    protected abstract void disposeContainer ();
+
+    /**
+     * If this method returns true we will run the application in the same JVM, otherwise we will
+     * fork off a new JVM. Some options are not supported if we do not fork off a new JVM.
+     */
+    protected boolean invokeDirect ()
+    {
+        return SysProps.direct();
+    }
+
+    /**
+     * Requests to show the document at the specified URL in a new window.
+     */
+    protected abstract void showDocument (String url);
+
+    /**
+     * Requests that Getdown exit.
+     */
+    protected abstract void exit (int exitCode);
+
+    /**
+     * Copies the supplied stream from the specified input to the specified output. Used to copy
+     * our child processes stderr and stdout to our own stderr and stdout.
+     */
+    protected static void copyStream (InputStream in, PrintStream out)
+    {
+        try {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+            String line;
+            while ((line = reader.readLine()) != null) {
+                out.print(line);
+                out.flush();
+            }
+        } catch (IOException ioe) {
+            log.warning("Failure copying", "in", in, "out", out, "error", ioe);
+        }
+    }
+
+    /** Used to fetch a progress report URL. */
+    protected class ProgressReporter extends Thread
+    {
+        public ProgressReporter (URL url) {
+            setDaemon(true);
+            _url = url;
+        }
+
+        @Override
+        public void run () {
+            try {
+                HttpURLConnection ucon = ConnectionUtil.openHttp(_app.proxy, _url, 0, 0);
+
+                // if we have a tracking cookie configured, configure the request with it
+                if (_app.getTrackingCookieName() != null &&
+                    _app.getTrackingCookieProperty() != null) {
+                    String val = System.getProperty(_app.getTrackingCookieProperty());
+                    if (val != null) {
+                        ucon.setRequestProperty("Cookie", _app.getTrackingCookieName() + "=" + val);
+                    }
+                }
+
+                // now request our tracking URL and ensure that we get a non-error response
+                ucon.connect();
+                try {
+                    if (ucon.getResponseCode() != HttpURLConnection.HTTP_OK) {
+                        log.warning("Failed to report tracking event",
+                            "url", _url, "rcode", ucon.getResponseCode());
+                    }
+                } finally {
+                    ucon.disconnect();
+                }
+
+            } catch (IOException ioe) {
+                log.warning("Failed to report tracking event", "url", _url, "error", ioe);
+            }
+        }
+
+        protected URL _url;
+    }
+
+    /** Used to pass progress on to our user interface. */
+    protected ProgressObserver _progobs = new ProgressObserver() {
+        public void progress (int percent) {
+            setStatusAsync(null, stepToGlobalPercent(percent), -1L, false);
+        }
+    };
+    
+    // Asynchronous or synchronous progress updates
+    protected void EQinvoke(Runnable r) {
+      
+      try {
+        readConfig(false);
+      } catch (Exception e) {
+        log.warning("Could't read config when invoking GUI action", "Exception", e.getMessage());
+      }
+      if (_ifc != null && (_shownContainer?_ifc.progressSyncAfterShown:_ifc.progressSync)) {
+        try {
+          EventQueue.invokeAndWait(r);
+          //r.run();
+        } catch (Exception e) {
+          log.warning("Tried to invokeAndWait but couldn't. Going to invokeLater instead", "Exception", e.getMessage());
+          EventQueue.invokeLater(r);
+        }
+      } else {
+        EventQueue.invokeLater(r);
+        //r.run();
+      }
+      
+      //try { Thread.sleep(500); } catch (Exception e) {}
+      
+    }
+    
+    protected Application _app;
+    protected Application.UpdateInterface _ifc = new Application.UpdateInterface(Config.EMPTY);
+
+    protected ResourceBundle _msgs;
+    protected Container _container;
+    protected JLayeredPane _layers;
+    protected JPanel instantSplashPane;
+    protected StatusPanel _status;
+    protected JButton _patchNotes;
+    protected AbortPanel _abort;
+    protected RotatingBackgrounds _background;
+
+    protected boolean _addedStatusLayer;
+    protected boolean _dead;
+    protected boolean _silent;
+    protected boolean _launchInSilent;
+    protected boolean _noUpdate;
+    protected long _startup;
+    protected Set<Resource> _toInstallResources;
+    protected boolean _readyToInstall;
+
+    protected boolean _enableTracking = true;
+    protected int _reportedProgress = 0;
+
+    protected boolean _shownContainer;
+
+    /** Number of minutes to wait after startup before beginning any real heavy lifting. */
+    protected int _delay;
+
+    protected int _stepMaxPercent;
+    protected int _stepMinPercent;
+    protected int _lastGlobalPercent;
+    protected int _uiDisplayPercent;
+
+    protected static final int MAX_LOOPS = 5;
+    protected static final long FALLBACK_CHECK_TIME = 1000L;
+    
+}
diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java
new file mode 100644 (file)
index 0000000..7c54f80
--- /dev/null
@@ -0,0 +1,301 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.launcher;
+
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Image;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.AbstractAction;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.KeyStroke;
+import javax.swing.WindowConstants;
+
+import com.samskivert.swing.util.SwingUtil;
+import com.threerings.getdown.data.Application;
+import com.threerings.getdown.data.Build;
+import com.threerings.getdown.data.EnvConfig;
+import com.threerings.getdown.data.SysProps;
+import com.threerings.getdown.util.LaunchUtil;
+import com.threerings.getdown.util.StringUtil;
+import static com.threerings.getdown.Log.log;
+import jalview.bin.StartupNotificationListener;
+
+/**
+ * The main application entry point for Getdown.
+ */
+public class GetdownApp
+{
+  public static String startupFilesParameterString = "";
+  /**
+   * The main entry point of the Getdown launcher application.
+   */
+  public static void main (String[] argv) {
+    try {
+      start(argv);
+    } catch (Exception e) {
+      log.warning("main() failed.", e);
+    }
+  }
+
+  /**
+   * Runs Getdown as an application, using the arguments supplie as {@code argv}.
+   * @return the {@code Getdown} instance that is running. {@link Getdown#start} will have been
+   * called on it.
+   * @throws Exception if anything goes wrong starting Getdown.
+   */
+  public static Getdown start (String[] argv) throws Exception {
+    List<EnvConfig.Note> notes = new ArrayList<>();
+    EnvConfig envc = EnvConfig.create(argv, notes);
+    if (envc == null) {
+      if (!notes.isEmpty()) for (EnvConfig.Note n : notes) System.err.println(n.message);
+      else System.err.println("Usage: java -jar getdown.jar [app_dir] [app_id] [app args]");
+      System.exit(-1);
+    }
+
+    // pipe our output into a file in the application directory
+    if (!SysProps.noLogRedir()) {
+      File logFile = new File(envc.appDir, "launcher.log");
+      try {
+        PrintStream logOut = new PrintStream(
+                new BufferedOutputStream(new FileOutputStream(logFile)), true);
+        System.setOut(logOut);
+        System.setErr(logOut);
+      } catch (IOException ioe) {
+        log.warning("Unable to redirect output to '" + logFile + "': " + ioe);
+      }
+    }
+
+    // report any notes from reading our env config, and abort if necessary
+    boolean abort = false;
+    for (EnvConfig.Note note : notes) {
+      switch (note.level) {
+      case INFO: log.info(note.message); break;
+      case WARN: log.warning(note.message); break;
+      case ERROR: log.error(note.message); abort = true; break;
+      }
+    }
+    if (abort) System.exit(-1);
+
+    log.info("Starting ...");
+    try
+    {
+      jalview.bin.StartupNotificationListener.setListener();
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    } catch (NoClassDefFoundError e)
+    {
+      log.warning("Starting without install4j classes");
+    } catch (Throwable t)
+    {
+      t.printStackTrace();
+    }
+    
+    // record a few things for posterity
+    log.info("------------------ VM Info ------------------");
+    log.info("-- OS Name: " + System.getProperty("os.name"));
+    log.info("-- OS Arch: " + System.getProperty("os.arch"));
+    log.info("-- OS Vers: " + System.getProperty("os.version"));
+    log.info("-- Java Vers: " + System.getProperty("java.version"));
+    log.info("-- Java Home: " + System.getProperty("java.home"));
+    log.info("-- User Name: " + System.getProperty("user.name"));
+    log.info("-- User Home: " + System.getProperty("user.home"));
+    log.info("-- Cur dir: " + System.getProperty("user.dir"));
+       log.info("-- launcher version: "+Build.version());
+    log.info("-- startupFilesParameterString: " + startupFilesParameterString);
+    log.info("---------------------------------------------");
+
+    Getdown app = new Getdown(envc) {
+      @Override
+      protected Container createContainer () {
+        // create our user interface, and display it
+        if (_frame == null) {
+          _frame = new JFrame("");
+          _frame.addWindowListener(new WindowAdapter() {
+            @Override
+            public void windowClosing (WindowEvent evt) {
+              handleWindowClose();
+            }
+          });
+          
+          // keep_on_top
+          try {
+                 readConfig(false);
+          } catch (Exception e) {
+                 log.warning("Error reading config for keep_on_top");
+          }
+          // move window to top, always on top
+          if (_ifc.keepOnTop) {
+                 log.info("Keep on top set to ", "keep_on_top", _ifc.keepOnTop);
+                         _frame.setAlwaysOnTop(true);
+          }
+
+          // handle close on ESC
+          String cancelId = "Cancel"; // $NON-NLS-1$
+          _frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
+                  KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelId);
+          _frame.getRootPane().getActionMap().put(cancelId, new AbstractAction() {
+            public void actionPerformed (ActionEvent e) {
+              handleWindowClose();
+            }
+          });
+          // this cannot be called in configureContainer as it is only allowed before the
+          // frame has been displayed for the first time
+          _frame.setUndecorated(_ifc.hideDecorations);
+          _frame.setResizable(false);
+        } else {
+          _frame.getContentPane().removeAll();
+        }
+        _frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+        return _frame.getContentPane();
+      }
+
+      @Override
+      protected void configureContainer () {
+        if (_frame == null) return;
+
+        _frame.setTitle(_ifc.name);
+
+        try {
+          _frame.setBackground(new Color(_ifc.background, true));
+        } catch (Exception e) {
+          log.warning("Failed to set background", "bg", _ifc.background, e);
+        }
+
+        if (_ifc.iconImages != null && _ifc.iconImages.size() > 0) {
+          ArrayList<Image> icons = new ArrayList<>();
+          for (String path : _ifc.iconImages) {
+            Image img = loadImage(path);
+            if (img == null) {
+              log.warning("Error loading icon image", "path", path);
+            } else {
+              icons.add(img);
+            }
+          }
+          if (icons.isEmpty()) {
+            log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
+          } else {
+            _frame.setIconImages(icons);
+          }
+        }
+      }
+
+      @Override
+      protected void showContainer () {
+        if (_frame != null) {
+          _frame.pack();
+          SwingUtil.centerWindow(_frame);
+          _frame.setVisible(true);
+        }
+        _shownContainer = true;
+      }
+
+      @Override
+      protected void disposeContainer () {
+        if (_frame != null) {
+          _frame.dispose();
+          _frame = null;
+        }
+      }
+
+      @Override
+      protected void showDocument (String url) {
+        if (!StringUtil.couldBeValidUrl(url)) {
+          // command injection would be possible if we allowed e.g. spaces and double quotes
+          log.warning("Invalid document URL.", "url", url);
+          return;
+        }
+        String[] cmdarray;
+        if (LaunchUtil.isWindows()) {
+          String osName = System.getProperty("os.name", "");
+          if (osName.indexOf("9") != -1 || osName.indexOf("Me") != -1) {
+            cmdarray = new String[] {
+                "command.com", "/c", "start", "\"" + url + "\"" };
+          } else {
+            cmdarray = new String[] {
+                "cmd.exe", "/c", "start", "\"\"", "\"" + url + "\"" };
+          }
+        } else if (LaunchUtil.isMacOS()) {
+          cmdarray = new String[] { "open", url };
+        } else { // Linux, Solaris, etc.
+          cmdarray = new String[] { "firefox", url };
+        }
+        try {
+          Runtime.getRuntime().exec(cmdarray);
+        } catch (Exception e) {
+          log.warning("Failed to open browser.", "cmdarray", cmdarray, e);
+        }
+      }
+
+      @Override
+      protected void exit (int exitCode) {
+        // if we're running the app in the same JVM, don't call System.exit, but do
+        // make double sure that the download window is closed.
+        if (invokeDirect()) {
+          disposeContainer();
+        } else {
+          System.exit(exitCode);
+        }
+      }
+
+      @Override
+      protected void fail (String message) {
+        super.fail(message);
+        // super.fail causes the UI to be created (if needed) on the next UI tick, so we
+        // want to wait until that happens before we attempt to redecorate the window
+        EQinvoke(new Runnable() {
+          @Override
+          public void run() {
+            // if the frame was set to be undecorated, make window decoration available
+            // to allow the user to close the window
+            if (_frame != null && _frame.isUndecorated()) {
+              _frame.dispose();
+              Color bg = _frame.getBackground();
+              if (bg != null && bg.getAlpha() < 255) {
+                // decorated windows do not allow alpha backgrounds
+                _frame.setBackground(
+                        new Color(bg.getRed(), bg.getGreen(), bg.getBlue()));
+              }
+              _frame.setUndecorated(false);
+              showContainer();
+            }
+          }
+        });
+      }
+
+      protected JFrame _frame;
+    };
+    
+    String startupFile = getStartupFilesParameterString();
+    if (!StringUtil.isBlank(startupFile)) {
+      Application.setStartupFilesFromParameterString(startupFile);
+    }
+    app.start();
+    return app;
+  }
+  
+  public static void setStartupFilesParameterString(String parameters) {
+    startupFilesParameterString = parameters;
+  }
+  
+  public static String getStartupFilesParameterString() {
+    return startupFilesParameterString;
+  }
+}
diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/MultipleGetdownRunning.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/MultipleGetdownRunning.java
new file mode 100644 (file)
index 0000000..5ac7449
--- /dev/null
@@ -0,0 +1,20 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.launcher;
+
+import java.io.IOException;
+
+/**
+ * Thrown when it's detected that multiple instances of the same getdown installer are running.
+ */
+public class MultipleGetdownRunning extends IOException
+{
+    public MultipleGetdownRunning ()
+    {
+        super("m.another_getdown_running");
+    }
+
+}
diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java
new file mode 100644 (file)
index 0000000..2178273
--- /dev/null
@@ -0,0 +1,195 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.launcher;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+
+import com.samskivert.swing.GroupLayout;
+import com.samskivert.swing.Spacer;
+import com.samskivert.swing.VGroupLayout;
+import com.threerings.getdown.util.MessageUtil;
+import static com.threerings.getdown.Log.log;
+
+/**
+ * Displays an interface with which the user can configure their proxy
+ * settings.
+ */
+public final class ProxyPanel extends JPanel implements ActionListener
+{
+    public ProxyPanel (Getdown getdown, ResourceBundle msgs)
+    {
+        _getdown = getdown;
+        _msgs = msgs;
+
+        setLayout(new VGroupLayout());
+        setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+        add(new SaneLabelField(get("m.configure_proxy")));
+        add(new Spacer(5, 5));
+
+        JPanel row = new JPanel(new GridLayout());
+        row.add(new SaneLabelField(get("m.proxy_host")), BorderLayout.WEST);
+        row.add(_host = new SaneTextField());
+        add(row);
+
+        row = new JPanel(new GridLayout());
+        row.add(new SaneLabelField(get("m.proxy_port")), BorderLayout.WEST);
+        row.add(_port = new SaneTextField());
+        add(row);
+
+        add(new Spacer(5, 5));
+
+        row = new JPanel(new GridLayout());
+        row.add(new SaneLabelField(get("m.proxy_auth_required")), BorderLayout.WEST);
+        _useAuth = new JCheckBox();
+        row.add(_useAuth);
+        add(row);
+
+        row = new JPanel(new GridLayout());
+        row.add(new SaneLabelField(get("m.proxy_username")), BorderLayout.WEST);
+        _username = new SaneTextField();
+        _username.setEnabled(false);
+        row.add(_username);
+        add(row);
+
+        row = new JPanel(new GridLayout());
+        row.add(new SaneLabelField(get("m.proxy_password")), BorderLayout.WEST);
+        _password = new SanePasswordField();
+        _password.setEnabled(false);
+        row.add(_password);
+        add(row);
+
+        _useAuth.addItemListener(new ItemListener() {
+            @Override public void itemStateChanged (ItemEvent event) {
+                boolean selected = (event.getStateChange() == ItemEvent.SELECTED);
+                _username.setEnabled(selected);
+                _password.setEnabled(selected);
+            }
+        });
+
+        add(new Spacer(5, 5));
+
+        row = GroupLayout.makeButtonBox(GroupLayout.CENTER);
+        JButton button;
+        row.add(button = new JButton(get("m.proxy_ok")));
+        button.setActionCommand("ok");
+        button.addActionListener(this);
+        row.add(button = new JButton(get("m.proxy_cancel")));
+        button.setActionCommand("cancel");
+        button.addActionListener(this);
+        add(row);
+    }
+
+    public void setProxy (String host, String port) {
+        if (host != null) {
+            _host.setText(host);
+        }
+        if (port != null) {
+            _port.setText(port);
+        }
+    }
+
+    // documentation inherited
+    @Override
+    public void addNotify ()
+    {
+        super.addNotify();
+        _host.requestFocusInWindow();
+    }
+
+    // documentation inherited
+    @Override
+    public Dimension getPreferredSize ()
+    {
+        // this is annoyingly hardcoded, but we can't just force the width
+        // or the JLabel will claim a bogus height thinking it can lay its
+        // text out all on one line which will booch the whole UI's
+        // preferred size
+        return new Dimension(500, 320);
+    }
+
+    // documentation inherited from interface
+    @Override
+    public void actionPerformed (ActionEvent e)
+    {
+        String cmd = e.getActionCommand();
+        if (cmd.equals("ok")) {
+            String user = null, pass = null;
+            if (_useAuth.isSelected()) {
+                user = _username.getText();
+                // we have to keep the proxy password around for every HTTP request, so having it
+                // in a char[] that gets zeroed out after use is not viable for this use case
+                pass = new String(_password.getPassword());
+            }
+            _getdown.configProxy(_host.getText(), _port.getText(), user, pass);
+        } else {
+            // they canceled, we're outta here
+            System.exit(0);
+        }
+    }
+
+    /** Used to look up localized messages. */
+    protected String get (String key)
+    {
+        // if this string is tainted, we don't translate it, instead we
+        // simply remove the taint character and return it to the caller
+        if (MessageUtil.isTainted(key)) {
+            return MessageUtil.untaint(key);
+        }
+        try {
+            return _msgs.getString(key);
+        } catch (MissingResourceException mre) {
+            log.warning("Missing translation message '" + key + "'.");
+            return key;
+        }
+    }
+
+    protected static class SaneLabelField extends JLabel {
+        public SaneLabelField(String message) { super(message); }
+        @Override public Dimension getPreferredSize () {
+            return clampWidth(super.getPreferredSize(), 200);
+        }
+    }
+    protected static class SaneTextField extends JTextField {
+        @Override public Dimension getPreferredSize () {
+            return clampWidth(super.getPreferredSize(), 150);
+        }
+    }
+    protected static class SanePasswordField extends JPasswordField {
+        @Override public Dimension getPreferredSize () {
+            return clampWidth(super.getPreferredSize(), 150);
+        }
+    }
+
+    protected static Dimension clampWidth (Dimension dim, int minWidth) {
+        dim.width = Math.max(dim.width, minWidth);
+        return dim;
+    }
+
+    protected Getdown _getdown;
+    protected ResourceBundle _msgs;
+
+    protected JTextField _host;
+    protected JTextField _port;
+    protected JCheckBox _useAuth;
+    protected JTextField _username;
+    protected JPasswordField _password;
+}
diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java
new file mode 100644 (file)
index 0000000..a36b5fa
--- /dev/null
@@ -0,0 +1,210 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.launcher;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.Authenticator;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.PasswordAuthentication;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Iterator;
+import java.util.ServiceLoader;
+
+import ca.beq.util.win32.registry.RegistryKey;
+import ca.beq.util.win32.registry.RegistryValue;
+import ca.beq.util.win32.registry.RootKey;
+
+import com.threerings.getdown.data.Application;
+import com.threerings.getdown.spi.ProxyAuth;
+import com.threerings.getdown.util.Config;
+import com.threerings.getdown.util.ConnectionUtil;
+import com.threerings.getdown.util.LaunchUtil;
+import com.threerings.getdown.util.StringUtil;
+
+import static com.threerings.getdown.Log.log;
+
+public class ProxyUtil {
+
+    public static boolean autoDetectProxy (Application app)
+    {
+        String host = null, port = null;
+
+        // check for a proxy configured via system properties
+        if (System.getProperty("https.proxyHost") != null) {
+            host = System.getProperty("https.proxyHost");
+            port = System.getProperty("https.proxyPort");
+        }
+        if (StringUtil.isBlank(host) && System.getProperty("http.proxyHost") != null) {
+            host = System.getProperty("http.proxyHost");
+            port = System.getProperty("http.proxyPort");
+        }
+
+        // check the Windows registry
+        if (StringUtil.isBlank(host) && LaunchUtil.isWindows()) {
+            try {
+                String rhost = null, rport = null;
+                boolean enabled = false;
+                RegistryKey.initialize();
+                RegistryKey r = new RegistryKey(RootKey.HKEY_CURRENT_USER, PROXY_REGISTRY);
+                for (Iterator<?> iter = r.values(); iter.hasNext(); ) {
+                    RegistryValue value = (RegistryValue)iter.next();
+                    if (value.getName().equals("ProxyEnable")) {
+                        enabled = value.getStringValue().equals("1");
+                    }
+                    if (value.getName().equals("ProxyServer")) {
+                        String strval = value.getStringValue();
+                        int cidx = strval.indexOf(":");
+                        if (cidx != -1) {
+                            rport = strval.substring(cidx+1);
+                            strval = strval.substring(0, cidx);
+                        }
+                        rhost = strval;
+                    }
+                }
+                if (enabled) {
+                    host = rhost;
+                    port = rport;
+                } else {
+                    log.info("Detected no proxy settings in the registry.");
+                }
+            } catch (Throwable t) {
+                log.info("Failed to find proxy settings in Windows registry", "error", t);
+            }
+        }
+
+        // look for a proxy.txt file
+        if (StringUtil.isBlank(host)) {
+            String[] hostPort = loadProxy(app);
+            host = hostPort[0];
+            port = hostPort[1];
+        }
+
+        if (StringUtil.isBlank(host)) {
+            return false;
+        }
+
+        // yay, we found a proxy configuration, configure it in the app
+        initProxy(app, host, port, null, null);
+        return true;
+    }
+
+    public static boolean canLoadWithoutProxy (URL rurl)
+    {
+        log.info("Testing whether proxy is needed, via: " + rurl);
+        try {
+            // try to make a HEAD request for this URL (use short connect and read timeouts)
+            URLConnection conn = ConnectionUtil.open(Proxy.NO_PROXY, rurl, 5, 5);
+            if (conn instanceof HttpURLConnection) {
+                HttpURLConnection hcon = (HttpURLConnection)conn;
+                try {
+                    hcon.setRequestMethod("HEAD");
+                    hcon.connect();
+                    // make sure we got a satisfactory response code
+                    int rcode = hcon.getResponseCode();
+                    if (rcode == HttpURLConnection.HTTP_PROXY_AUTH ||
+                        rcode == HttpURLConnection.HTTP_FORBIDDEN) {
+                        log.warning("Got an 'HTTP credentials needed' response", "code", rcode);
+                    } else {
+                        return true;
+                    }
+                } finally {
+                    hcon.disconnect();
+                }
+            } else {
+                // if the appbase is not an HTTP/S URL (like file:), then we don't need a proxy
+                return true;
+            }
+        } catch (IOException ioe) {
+            log.info("Failed to HEAD " + rurl + ": " + ioe);
+            log.info("We probably need a proxy, but auto-detection failed.");
+        }
+        return false;
+    }
+
+    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);
+
+        // save our credentials via the SPI
+        if (!StringUtil.isBlank(username) && !StringUtil.isBlank(password)) {
+            ServiceLoader<ProxyAuth> loader = ServiceLoader.load(ProxyAuth.class);
+            Iterator<ProxyAuth> iterator = loader.iterator();
+            String appDir = app.getAppDir().getAbsolutePath();
+            while (iterator.hasNext()) {
+                iterator.next().saveCredentials(appDir, username, password);
+            }
+        }
+
+        // also configure them in the app
+        initProxy(app, host, port, username, password);
+    }
+
+    public static String[] loadProxy (Application app) {
+        File pfile = app.getLocalPath("proxy.txt");
+        if (pfile.exists()) {
+            try {
+                Config pconf = Config.parseConfig(pfile, Config.createOpts(false));
+                return new String[] { pconf.getString("host"), pconf.getString("port") };
+            } catch (IOException ioe) {
+                log.warning("Failed to read '" + pfile + "': " + ioe);
+            }
+        }
+        return new String[] { null, null};
+    }
+
+    public static void saveProxy (Application app, String host, String port) {
+        File pfile = app.getLocalPath("proxy.txt");
+        try (PrintStream pout = new PrintStream(new FileOutputStream(pfile))) {
+            if (!StringUtil.isBlank(host)) {
+                pout.println("host = " + host);
+            }
+            if (!StringUtil.isBlank(port)) {
+                pout.println("port = " + port);
+            }
+        } catch (IOException ioe) {
+            log.warning("Error creating proxy file '" + pfile + "': " + ioe);
+        }
+    }
+
+    public static void initProxy (Application app, String host, String port,
+                                  String username, String password)
+    {
+        // check whether we have saved proxy credentials
+        String appDir = app.getAppDir().getAbsolutePath();
+        ServiceLoader<ProxyAuth> loader = ServiceLoader.load(ProxyAuth.class);
+        Iterator<ProxyAuth> iter = loader.iterator();
+        ProxyAuth.Credentials creds = iter.hasNext() ? iter.next().loadCredentials(appDir) : null;
+        if (creds != null) {
+            username = creds.username;
+            password = creds.password;
+        }
+        boolean haveCreds = !StringUtil.isBlank(username) && !StringUtil.isBlank(password);
+
+        int pport = StringUtil.isBlank(port) ? 80 : Integer.valueOf(port);
+        log.info("Using proxy", "host", host, "port", pport, "haveCreds", haveCreds);
+        app.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, pport));
+
+        if (haveCreds) {
+            final String fuser = username;
+            final char[] fpass = password.toCharArray();
+            Authenticator.setDefault(new Authenticator() {
+                @Override protected PasswordAuthentication getPasswordAuthentication () {
+                    return new PasswordAuthentication(fuser, fpass);
+                }
+            });
+        }
+    }
+
+    protected static final String PROXY_REGISTRY =
+        "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
+}
diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java
new file mode 100644 (file)
index 0000000..d3aa2bd
--- /dev/null
@@ -0,0 +1,132 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.launcher;
+
+import java.awt.Image;
+import java.util.List;
+
+import static com.threerings.getdown.Log.log;
+
+public final class RotatingBackgrounds
+{
+    public interface ImageLoader {
+        /** Loads and returns the image with the supplied path. */
+        public Image loadImage (String path);
+    }
+
+    /**
+     * Creates a placeholder if there are no images. Just returns null from getImage every time.
+     */
+    public RotatingBackgrounds ()
+    {
+        makeEmpty();
+    }
+
+    /** Creates a single image background. */
+    public RotatingBackgrounds (Image background)
+    {
+        percentages = new int[] { 0 };
+        minDisplayTime = new int[] { 0 };
+        images = new Image[] { background };
+        errorImage = images[0];
+    }
+
+    /**
+     * Create a sequence of images to be rotated through from <code>backgrounds</code>.
+     *
+     * Each String in backgrounds should be the path to the image, a semicolon, and the minimum
+     * amount of time to display the image in seconds. Each image will be active for an equal
+     * percentage of the download process, unless one hasn't been active for its minimum display
+     * time when the next should be shown. In that case, it's left up until its been there for its
+     * minimum display time and then the next one gets to come up.
+     */
+    public RotatingBackgrounds (List<String> backgrounds, String errorBackground, ImageLoader loader)
+    {
+        percentages = new int[backgrounds.size()];
+        minDisplayTime = new int[backgrounds.size()];
+        images = new Image[backgrounds.size()];
+        for (int ii = 0; ii < backgrounds.size(); ii++) {
+            String background = backgrounds.get(ii);
+            String[] pieces = background.split(";");
+            if (pieces.length != 2) {
+                log.warning("Unable to parse background image '" + background + "'");
+                makeEmpty();
+                return;
+            }
+            images[ii] = loader.loadImage(pieces[0]);
+            try {
+                minDisplayTime[ii] = Integer.parseInt(pieces[1]);
+            } catch (NumberFormatException e) {
+                log.warning("Unable to parse background image display time '" + background + "'");
+                makeEmpty();
+                return;
+            }
+            percentages[ii] = (int)((ii/(float)backgrounds.size()) * 100);
+        }
+        if (errorBackground == null) {
+            errorImage = images[0];
+        } else {
+            errorImage = loader.loadImage(errorBackground);
+        }
+    }
+
+    /**
+     * @return the image to display at the given progress or null if there aren't any.
+     */
+    public Image getImage (int progress)
+    {
+        if (images.length == 0) {
+            return null;
+        }
+        long now = System.currentTimeMillis();
+        if (current != images.length - 1
+            && (current == -1 || (progress >= percentages[current + 1] &&
+                    (now - currentDisplayStart) / 1000 > minDisplayTime[current]))) {
+            current++;
+            currentDisplayStart = now;
+        }
+        return images[current];
+    }
+
+    /**
+     * Returns the image to display if an error has caused getdown to fail.
+     */
+    public Image getErrorImage ()
+    {
+        return errorImage;
+    }
+
+    /**
+     * @return the number of images in this RotatingBackgrounds
+     */
+    public int getNumImages() {
+        return images.length;
+    }
+
+    protected void makeEmpty ()
+    {
+        percentages = new int[] {};
+        minDisplayTime = new int[] {};
+        images = new Image[] {};
+    }
+
+    /** Time at which the currently displayed image was first displayed in millis. */
+    protected long currentDisplayStart;
+
+    /** The index of the currently displayed image or -1 if we haven't displayed any. */
+    protected int current = -1;
+
+    protected Image[] images;
+
+    /** The image to display if getdown has failed due to an error. */
+    protected Image errorImage;
+
+    /** Percentage at which each image should be displayed. */
+    protected int[] percentages;
+
+    /** Time to show each image in seconds. */
+    protected int[] minDisplayTime;
+}
diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java
new file mode 100644 (file)
index 0000000..620182b
--- /dev/null
@@ -0,0 +1,418 @@
+//
+// Getdown - application installer, patcher and launcher
+// Copyright (C) 2004-2018 Getdown authors
+// https://github.com/threerings/getdown/blob/master/LICENSE
+
+package com.threerings.getdown.launcher;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.image.ImageObserver;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.swing.JComponent;
+import javax.swing.Timer;
+
+import com.samskivert.swing.Label;
+import com.samskivert.swing.LabelStyleConstants;
+import com.samskivert.swing.util.SwingUtil;
+import com.samskivert.util.Throttle;
+
+import com.threerings.getdown.data.Application.UpdateInterface;
+import com.threerings.getdown.data.Build;
+import com.threerings.getdown.util.MessageUtil;
+import com.threerings.getdown.util.Rectangle;
+import com.threerings.getdown.util.StringUtil;
+
+import static com.threerings.getdown.Log.log;
+
+/**
+ * Displays download and patching status.
+ */
+public final class StatusPanel extends JComponent
+    implements ImageObserver
+{
+    public StatusPanel (ResourceBundle msgs)
+    {
+        _msgs = msgs;
+
+        // Add a bit of "throbbing" to the display by updating the number of dots displayed after
+        // our status. This lets users know things are still working.
+        _timer = new Timer(1000,
+            new ActionListener() {
+                public void actionPerformed (ActionEvent event) {
+                    if (_status != null && !_displayError) {
+                        _statusDots = (_statusDots % 3) + 1; // 1, 2, 3, 1, 2, 3, etc.
+                        updateStatusLabel();
+                    }
+                }
+            });
+    }
+
+    public void init (UpdateInterface ifc, RotatingBackgrounds bg, Image barimg)
+    {
+        _ifc = ifc;
+        _bg = bg;
+        Image img = _bg.getImage(_progress);
+        int width = img == null ? -1 : img.getWidth(this);
+        int height = img == null ? -1 : img.getHeight(this);
+        if (width == -1 || height == -1) {
+            Rectangle bounds = ifc.progress.union(ifc.status);
+            // assume the x inset defines the frame padding; add it on the left, right, and bottom
+            _psize = new Dimension(bounds.x + bounds.width + bounds.x,
+                                   bounds.y + bounds.height + bounds.x);
+        } else {
+            _psize = new Dimension(width, height);
+        }
+        _barimg = barimg;
+        invalidate();
+    }
+
+    @Override
+    public boolean imageUpdate (Image img, int infoflags, int x, int y, int width, int height)
+    {
+        boolean updated = false;
+        if ((infoflags & WIDTH) != 0) {
+            _psize.width = width;
+            updated = true;
+        }
+        if ((infoflags & HEIGHT) != 0) {
+            _psize.height = height;
+            updated = true;
+        }
+        if (updated) {
+            invalidate();
+            setSize(_psize);
+            getParent().setSize(_psize);
+        }
+        return (infoflags & ALLBITS) == 0;
+    }
+
+    /**
+     * Adjusts the progress display to the specified percentage.
+     */
+    public void setProgress (int percent, long remaining)
+    {
+        boolean needsRepaint = false;
+
+        // maybe update the progress label
+        if (_progress != percent) {
+            _progress = percent;
+            if (_ifc != null && !_ifc.hideProgressText) {
+                String msg = MessageFormat.format(get("m.complete"), percent);
+                _newplab = createLabel(msg, new Color(_ifc.progressText, true));
+            }
+            needsRepaint = true;
+        }
+
+        // maybe update the remaining label
+        if (remaining > 1) {
+            // skip this estimate if it's been less than a second since our last one came in
+            if (!_rthrottle.throttleOp()) {
+                _remain[_ridx++%_remain.length] = remaining;
+            }
+
+            // smooth the remaining time by taking the trailing average of the last four values
+            remaining = 0;
+            int values = Math.min(_ridx, _remain.length);
+            for (int ii = 0; ii < values; ii++) {
+                remaining += _remain[ii];
+            }
+            remaining /= values;
+
+            if (_ifc != null && !_ifc.hideProgressText) {
+                // now compute our display value
+                int minutes = (int)(remaining / 60), seconds = (int)(remaining % 60);
+                String remstr = minutes + ":" + ((seconds < 10) ? "0" : "") + seconds;
+                String msg = MessageFormat.format(get("m.remain"), remstr);
+                _newrlab = createLabel(msg, new Color(_ifc.statusText, true));
+            }
+            needsRepaint = true;
+
+        } else if (_rlabel != null || _newrlab != null) {
+            _rthrottle = new Throttle(1, 1000);
+            _ridx = 0;
+            _newrlab = _rlabel = null;
+            needsRepaint = true;
+        }
+
+        if (needsRepaint) {
+            repaint();
+        }
+    }
+
+    /**
+     * Displays the specified status string.
+     */
+    public void setStatus (String status, boolean displayError)
+    {
+        _status = xlate(status);
+        _displayError = displayError;
+        updateStatusLabel();
+    }
+
+    /**
+     * Stop the throbbing.
+     */
+    public void stopThrob ()
+    {
+        _timer.stop();
+        _statusDots = 3;
+        updateStatusLabel();
+    }
+
+    @Override
+    public void addNotify ()
+    {
+        super.addNotify();
+        _timer.start();
+    }
+
+    @Override
+    public void removeNotify ()
+    {
+        _timer.stop();
+        super.removeNotify();
+    }
+
+    // documentation inherited
+    @Override
+    public void paintComponent (Graphics g)
+    {
+        super.paintComponent(g);
+        Graphics2D gfx = (Graphics2D)g;
+
+        // attempt to draw a background image...
+        Image img;
+        if (_displayError) {
+            img = _bg.getErrorImage();
+        } else {
+            img = _bg.getImage(_progress);
+        }
+        if (img != null) {
+            gfx.drawImage(img, 0, 0, this);
+        }
+
+        Object oalias = SwingUtil.activateAntiAliasing(gfx);
+
+        // if we have new labels; lay them out
+        if (_newlab != null) {
+            _newlab.layout(gfx);
+            _label = _newlab;
+            _newlab = null;
+        }
+        if (_newplab != null) {
+            _newplab.layout(gfx);
+            _plabel = _newplab;
+            _newplab = null;
+        }
+        if (_newrlab != null) {
+            _newrlab.layout(gfx);
+            _rlabel = _newrlab;
+            _newrlab = null;
+        }
+
+        if (_barimg != null) {
+            gfx.setClip(_ifc.progress.x, _ifc.progress.y,
+                        _progress * _ifc.progress.width / 100,
+                        _ifc.progress.height);
+            gfx.drawImage(_barimg, _ifc.progress.x, _ifc.progress.y, null);
+            gfx.setClip(null);
+        } else {
+            gfx.setColor(new Color(_ifc.progressBar, true));
+            gfx.fillRect(_ifc.progress.x, _ifc.progress.y,
+                         _progress * _ifc.progress.width / 100,
+                         _ifc.progress.height);
+        }
+
+        if (_plabel != null) {
+            int xmarg = (_ifc.progress.width - _plabel.getSize().width)/2;
+            int ymarg = (_ifc.progress.height - _plabel.getSize().height)/2;
+            _plabel.render(gfx, _ifc.progress.x + xmarg, _ifc.progress.y + ymarg);
+        }
+
+        if (_label != null) {
+            _label.render(gfx, _ifc.status.x, getStatusY(_label));
+        }
+
+        if (_rlabel != null) {
+            // put the remaining label at the end of the status area. This could be dangerous
+            // but I think the only time we would display it is with small statuses.
+            int x = _ifc.status.x + _ifc.status.width - _rlabel.getSize().width;
+            _rlabel.render(gfx, x, getStatusY(_rlabel));
+        }
+
+        SwingUtil.restoreAntiAliasing(gfx, oalias);
+    }
+
+    // documentation inherited
+    @Override
+    public Dimension getPreferredSize ()
+    {
+        return _psize;
+    }
+
+    /**
+     * Update the status label.
+     */
+    protected void updateStatusLabel ()
+    {
+        if (_ifc == null) {
+               return;
+        }
+        String status = _status;
+        if (!_displayError) {
+            for (int ii = 0; ii < _statusDots; ii++) {
+                status += " .";
+            }
+        }
+       StringBuilder labelText = new StringBuilder();
+       if (_ifc.displayVersion) {
+               labelText.append("launcher version: "+Build.version());
+               labelText.append("\n");
+       }
+       if (_ifc.displayAppbase) {
+               labelText.append("appbase: "+_appbase);
+               labelText.append("\n");
+       }
+       labelText.append(status); 
+        
+        _newlab = createLabel(labelText.toString(), new Color(_ifc.statusText, true));
+        // set the width of the label to the width specified
+        int width = _ifc.status.width;
+        if (width == 0) {
+            // unless we had trouble reading that width, in which case use the entire window
+            width = getWidth();
+        }
+        // but the window itself might not be initialized and have a width of 0
+        if (width > 0) {
+            _newlab.setTargetWidth(width);
+        }
+        repaint();
+    }
+
+    /**
+     * Get the y coordinate of a label in the status area.
+     */
+    protected int getStatusY (Label label)
+    {
+        // if the status region is higher than the progress region, we
+        // want to align the label with the bottom of its region
+        // rather than the top
+        if (_ifc.status.y > _ifc.progress.y) {
+            return _ifc.status.y;
+        }
+        return _ifc.status.y + (_ifc.status.height - label.getSize().height);
+    }
+
+    /**
+     * Create a label, taking care of adding the shadow if needed.
+     */
+    protected Label createLabel (String text, Color color)
+    {
+        Label label = new Label(text, color, FONT);
+        if (_ifc.textShadow != 0) {
+            label.setAlternateColor(new Color(_ifc.textShadow, true));
+            label.setStyle(LabelStyleConstants.SHADOW);
+        }
+        return label;
+    }
+
+    /** Used by {@link #setStatus}. */
+    protected String xlate (String compoundKey)
+    {
+        // to be more efficient about creating unnecessary objects, we
+        // do some checking before splitting
+        int tidx = compoundKey.indexOf('|');
+        if (tidx == -1) {
+            return get(compoundKey);
+
+        } else {
+            String key = compoundKey.substring(0, tidx);
+            String argstr = compoundKey.substring(tidx+1);
+            String[] args = argstr.split("\\|");
+            // unescape and translate the arguments
+            for (int i = 0; i < args.length; i++) {
+                // if the argument is tainted, do no further translation
+                // (it might contain |s or other fun stuff)
+                if (MessageUtil.isTainted(args[i])) {
+                    args[i] = MessageUtil.unescape(MessageUtil.untaint(args[i]));
+                } else {
+                    args[i] = xlate(MessageUtil.unescape(args[i]));
+                }
+            }
+            return get(key, args);
+        }
+    }
+
+    /** Used by {@link #setStatus}. */
+    protected String get (String key, String[] args)
+    {
+        String msg = get(key);
+        if (msg != null) return MessageFormat.format(MessageUtil.escape(msg), (Object[])args);
+        return key + String.valueOf(Arrays.asList(args));
+    }
+
+    /** Used by {@link #setStatus}, and {@link #setProgress}. */
+    protected String get (String key)
+    {
+        // if we have no _msgs that means we're probably recovering from a
+        // failure to load the translation messages in the first place, so
+        // just give them their key back because it's probably an english
+        // string; whee!
+        if (_msgs == null) {
+            return key;
+        }
+
+        // if this string is tainted, we don't translate it, instead we
+        // simply remove the taint character and return it to the caller
+        if (MessageUtil.isTainted(key)) {
+            return MessageUtil.untaint(key);
+        }
+        try {
+            return _msgs.getString(key);
+        } catch (MissingResourceException mre) {
+            log.warning("Missing translation message '" + key + "'.");
+            return key;
+        }
+    }
+    
+    public void setAppbase(String appbase) {
+       _appbase = appbase;
+    }
+
+    protected Image _barimg;
+    protected RotatingBackgrounds _bg;
+    protected Dimension _psize;
+
+    protected ResourceBundle _msgs;
+
+    protected int _progress = -1;
+    protected String _status;
+    protected int _statusDots = 1;
+    protected boolean _displayError;
+    protected Label _label, _newlab;
+    protected Label _plabel, _newplab;
+    protected Label _rlabel, _newrlab;
+
+    protected UpdateInterface _ifc;
+    protected Timer _timer;
+
+    protected long[] _remain = new long[4];
+    protected int _ridx;
+    protected Throttle _rthrottle = new Throttle(1, 1000L);
+
+    protected static final Font FONT = new Font("SansSerif", Font.BOLD, 12);
+    
+    public String _appbase;
+}
diff --git a/getdown/src/getdown/launcher/src/main/java/jalview/bin/StartupNotificationListener.java b/getdown/src/getdown/launcher/src/main/java/jalview/bin/StartupNotificationListener.java
new file mode 100644 (file)
index 0000000..5c6c7c3
--- /dev/null
@@ -0,0 +1,29 @@
+package jalview.bin;
+
+import com.threerings.getdown.launcher.GetdownApp;
+import static com.threerings.getdown.Log.log;
+
+public class StartupNotificationListener {
+
+  public static void setListener() {
+
+    
+    try {
+      com.install4j.api.launcher.StartupNotification.registerStartupListener(
+        new com.install4j.api.launcher.StartupNotification.Listener() {
+          @Override
+          public void startupPerformed(String parameters) { 
+            log.info("StartupNotification.Listener.startupPerformed: '"+parameters+"'");
+            GetdownApp.setStartupFilesParameterString(parameters);
+          }
+        }
+      );
+    } catch (Exception e) {
+      e.printStackTrace();
+    } catch (NoClassDefFoundError t) {
+      log.warning("Starting without install4j classes");
+    }
+
+  }
+
+}
diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages.properties
new file mode 100644 (file)
index 0000000..bb31a7b
--- /dev/null
@@ -0,0 +1,114 @@
+#
+# $Id$
+#
+# Getdown translation messages
+
+m.abort_title = Abort installation?
+m.abort_confirm = <html>Are you sure you want to stop installation? \
+  You can resume at a later time by running the application again.</html>
+m.abort_ok = Quit
+m.abort_cancel = Continue installation
+
+m.detecting_proxy = Trying to auto-detect proxy settings
+
+m.configure_proxy = <html>We were unable to connect to the application server to download data. \
+  <p> Please make sure that no virus scanner or firewall is blocking network communicaton with \
+  the server. \
+  <p> Your computer may access the Internet through a proxy and we were unable to automatically \
+  detect your proxy settings. If you know your proxy settings, you can enter them below.</html>
+
+m.proxy_extra = <html>If you are sure that you don't use a proxy then \
+  perhaps there is a temporary Internet outage that is preventing us from \
+  communicating with the servers. In this case, you can cancel and try \
+  installing again later.</html>
+
+m.proxy_host = Proxy IP
+m.proxy_port = Proxy port
+m.proxy_username = Username
+m.proxy_password = Password
+m.proxy_auth_required = Authentication required
+m.proxy_ok = OK
+m.proxy_cancel = Cancel
+
+m.downloading_java = Downloading Java Virtual Machine
+m.unpacking_java = Unpacking Java Virtual Machine
+
+m.resolving = Resolving downloads
+m.downloading = Downloading data
+m.failure = Download failed: {0}
+
+m.checking = Checking for update
+m.validating = Validating
+m.patching = Patching
+m.launching = Launching
+
+m.patch_notes = Patch Notes
+m.play_again = Play Again
+
+m.complete = {0}% complete
+m.remain = {0} remaining
+
+m.updating_metadata = Downloading control files
+
+m.init_failed = Our configuration file is missing or corrupt. Attempting \
+  to download a new copy...
+
+m.java_download_failed = We were unable to automatically download the \
+  necessary version of Java for your computer.\n\n\
+  Please go to www.java.com and download the latest version of \
+  Java, then try running the application again.
+
+m.java_unpack_failed = We were unable to unpack an updated version of \
+  Java. Please make sure you have at least 100 MB of free space on your \
+  harddrive and try running the application again.\n\n\
+  If that does not solve the problem, go to www.java.com and download and \
+  install the latest version of Java and try again.
+
+m.unable_to_repair = We were unable to download the necessary files after \
+  five attempts. You can try running the application again, but if it \
+  fails you may need to uninstall and reinstall.
+
+m.unknown_error = The application has failed to launch due to some strange \
+  error from which we could not recover. Please visit\n{0} for information on \
+  how to recover.
+m.init_error = The application has failed to launch due to the following \
+  error:\n{0}\n\nPlease visit\n{1} for \
+  information on how to handle such problems.
+
+m.readonly_error = The directory in which this application is installed: \
+  \n{0}\nis read-only. Please install the application into a directory where \
+  you have write access.
+
+m.missing_resource = The application has failed to launch due to a missing \
+  resource:\n{0}\n\nPlease visit\n{1} for information on how to handle such \
+  problems.
+
+m.insufficient_permissions_error = You did not accept this application's \
+ digital signature. If you want to run the application, you will need to accept \
+ its digital signature.\n\nTo do so, you will need to quit your web browser, \
+ restart it, and return to this web page to relaunch the application. When the \
+ security dialog is shown, click the button to accept the digital signature \
+ and grant this application the privileges it needs to run.
+
+m.corrupt_digest_signature_error = We couldn't verify the application's digital \
+ signature.\nPlease check that you are launching the application from\nthe \
+ correct website.
+
+m.default_install_error = the support section of the website
+
+m.another_getdown_running = Multiple instances of this application's \
+ installer are running. This one will stop and let another complete.
+
+m.applet_stopped = Getdown's applet was told to stop working.
+
+# application/digest errors
+m.missing_appbase = The configuration file is missing the 'appbase'.
+m.invalid_version = The configuration file specifies an invalid version.
+m.invalid_appbase = The configuration file specifies an invalid 'appbase'.
+m.missing_class = The configuration file is missing the application class.
+m.missing_code = The configuration file specifies no code resources.
+m.invalid_digest_file = The digest file is invalid.
+
+# When at 0%
+m.initialising = Initialising
+m.starting = Starting
diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_de.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_de.properties
new file mode 100644 (file)
index 0000000..ff0a388
--- /dev/null
@@ -0,0 +1,120 @@
+#
+# $Id$
+#
+# Getdown translation messages
+
+m.abort_title = Installation abbrechen?
+m.abort_confirm = <html>Bist du sicher, dass du die Installation abbrechen \
+m\u00f6chtest? \
+  Du kannst sp\u00e4ter fortfahren, indem du die Anwendung erneut \
+ausf\u00fchrst.</html>
+m.abort_ok = Beenden
+m.abort_cancel = Installation fortsetzen
+
+m.detecting_proxy = Versuche Proxy-Einstellungen automatisch zu ermitteln
+
+m.configure_proxy = <html>Es konnte keine Verbindung zum Applikations-Server aufgebaut werden. \
+  <p>Bitte kontrollieren Sie die Proxyeinstellungen und stellen Sie sicher, dass keine lokal oder \
+  im Netzwerk betriebene Sicherheitsanwendung (Virenscanner, Firewall, etc.) die Kommunikation \
+  mit dem Server blockiert.<br> \
+  Wenn kein Proxy verwendet werden soll, l\u00f6schen Sie bitte alle Eintr\u00e4ge in den unten \
+  stehenden Feldern und klicken sie auf OK.</html>
+
+m.proxy_extra = <html>Sollten Sie keine Proxyeinstellungen gesetzt haben wenden Sie sich bitte \
+  an Ihren Administrator.</html>
+
+m.proxy_host = Proxy-Adresse
+m.proxy_port = Proxy-Port
+m.proxy_username = Benutzername
+m.proxy_password = Passwort
+m.proxy_auth_required = Authentisierung erforderlich
+m.proxy_ok = OK
+m.proxy_cancel = Abbrechen
+
+m.downloading_java = Lade Java Virtual Machine herunter
+m.unpacking_java = Entpacke Java Virtual Machine
+
+m.resolving = Bereite Download vor
+m.downloading = Lade Daten herunter
+m.failure = Download fehlgeschlagen: {0}
+
+m.checking = Suche nach Updates
+m.validating = Validiere Download
+m.patching = Patche
+m.launching = Starte
+
+m.patch_notes = Patchnotes
+
+m.complete = {0}% abgeschlossen
+m.remain = {0} \u00fcbrig
+
+m.updating_metadata = Lade Steuerungsdateien herunter
+
+m.init_failed = Unsere Konfigurationsdatei fehlt oder ist besch\u00e4digt. \
+Versuche, eine neue Kopie herunterzuladen...
+
+m.java_download_failed = Wir konnten die notwendige Javaversion f\u00fcr deinen \
+Computer nicht automatisch herunterladen. \n\n \
+Bitte auf www.java.com die aktuelle Javaversion herunterladen und dann die \
+Anwendung erneut starten.
+
+m.java_unpack_failed = Wir konnten die aktualisierte Javaversion nicht \
+entpacken. Bitte stelle sicher, dass wenigstens 100MB Platz auf der \
+Festplatte frei sind und versuche dann die Anwendung erneut zu \
+starten.\n\n\ \
+Falls das das Problem nicht beseitigt, bitte auf www.java.com die aktuelle \
+Javaversion herunterladen und installieren und dann erneut versuchen.
+
+m.unable_to_repair = Wir konnten die notwendigen Dateien nach 5 Versuchen \
+nicht herunterladen. Du kannst versuchen, die Anwendung erneut zu starten, \
+aber wenn dies erneut fehlschl\u00e4gt, musst du die Anwendung deinstallieren \
+und erneut installieren.
+
+m.unknown_error = Die Anwendung konnte wegen eines unbekannten Fehlers \
+nicht gestartet werden. Bitte auf \n{0} weiterlesen.
+
+m.init_error = Die Anwendung konnte wegen folgendem Fehler nicht gestartet \
+werden:\n{0}\n\n Bitte auf \n{1} weiterlesen, um zu erfahren, wie bei \
+solchen Problemen vorzugehen ist.
+
+m.readonly_error = Das Verzeichnis, in dem die Anwendung installiert ist: \
+ \n{0}\nist nicht schreibberechtigt. Bitte in ein Verzeichnis mit \
+Schreibzugriff installieren.
+
+m.missing_resource = Die Anwendung konnte nicht gestartet werden, da die \
+folgende Quelle nicht gefunden wurde:\n{0}\n\n\ Bitte auf \n{1} \
+weiterlesen, um zu erfahren, wie bei solchen Problemen vorzugehen ist.
+
+m.insufficient_permissions_error = Du hast die digitale Signatur dieser \
+Anwendung nicht akzeptiert. Falls du diese Anwendung benutzen willst, \
+musst du ihre digitale Signatur akzeptieren. \n\Um das zu tun, musst du \
+deinen Browser beenden, neu starten und erneut die Anwendung von dieser \
+Webseite aus starten. Wenn die Sicherheitsabfrage erscheint, bitte die \
+digitale Signatur akzeptieren, um der Anwendung die n\u00f6tigen Rechte zu \
+geben, die sie braucht, um zu laufen.
+
+m.corrupt_digest_signature_error = Wir konnten die digitale Signatur \
+dieser Anwendung nicht \u00fcberpr\u00fcfen.\nBitte \u00fcberpr\u00fcfe, ob du die Anwendung \
+von der richtigen Webseite aus startest.
+
+m.default_install_error = der Support-Webseite
+
+m.another_getdown_running = Diese Installationsanwendung l\u00e4uft in mehreren \
+Instanzen. Diese Instanz wird sich beenden und eine andere Instanz den \
+Vorgang erledigen lassen.
+
+m.applet_stopped = Die Anwendung wurde beendet.
+
+
+# application/digest errors
+m.missing_appbase = In der Konfigurationsdatei fehlt die 'appbase'.
+m.invalid_version = In der Konfigurationsdatei steht die falsche Version.
+m.invalid_appbase = In der Konfigurationsdatei steht die falsche 'appbase'.
+m.missing_class = In der Konfigurationsdatei fehlt die Anwendungsklasse.
+m.missing_code = Die Konfigurationsdatei enth\u00e4lt keine Codequellen.
+m.invalid_digest_file = Die Hashwertedatei ist ung\u00fcltig.
+
+
+# When at 0%
+m.initialising = Initialising
+m.starting = Starting
diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_es.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_es.properties
new file mode 100644 (file)
index 0000000..00acad0
--- /dev/null
@@ -0,0 +1,119 @@
+#
+# $Id$
+#
+# Getdown translation messages
+
+m.abort_title = \u00bfCancelar la instalaci\u00f3n?
+m.abort_confirm = <html>\u00bfEst\u00e1s seguro de querer cancelar la instalaci\u00f3n? \
+  Puedes continuarla despu\u00e9s si corres de nuevo la aplicaci\u00f3n.</html>
+m.abort_ok = Cancelar
+m.abort_cancel = Continuar la instalaci\u00f3n
+
+m.detecting_proxy = Detectando autom\u00e1ticamente la configuraci\u00f3n proxy
+
+m.configure_proxy = <html>No ha sido posible conectar con nuestros servidores para \
+  descargar los datos del juego. \
+  <ul><li> Si el cortafuegos de Windows o Norton Internet Security tiene instrucciones \
+  de bloquear <code>javaw.exe</code> no podemos descargar el juego. Necesitar\u00e1s \
+  permitir que <code>javaw.exe</code> tenga acceso al Internet. Puedes intentar \
+  correr el juego de nuevo, pero es posible que debas dar permisos a javaw.exe en la \
+  configuraci\u00f3n de tu cortafuegos ( Inicio -> Panel de control -> Firewall de Windows ).</ul> \
+  <p> Es posible que tu computadora tenga acceso al Internet por medio de un proxy por lo que \
+  no ha sido posible detectar autom\u00e1ticamente tu configuraci\u00f3n. Si conoces tu \
+  configuraci\u00f3n proxy, puedes anotarla abajo.</html>
+
+m.proxy_extra = <html>Si est\u00e1s seguro de que no tienes un proxy entonces \
+  tal vez exista un falla temporal en el Internet que est\u00e1 evitando que podamos \
+  comunicarnos con los servidores. En este caso, puedes cancelar e intentar \
+  instalarla de nuevo m\u00e1s tarde.</html>
+
+m.proxy_host = IP proxy
+m.proxy_port = Puerto proxy
+m.proxy_username = Nombre de usuario
+m.proxy_password = Contrase\u00f1a
+m.proxy_auth_required = Autenticacion requerida
+m.proxy_ok = OK
+m.proxy_cancel = Cancelar
+
+m.downloading_java = Descargando Java Virtual Machine
+m.unpacking_java = Desempacando Java Virtual Machine
+
+m.resolving = Resolviendo descarga
+m.downloading = Descargando datos
+m.failure = Descarga fallida: {0}
+
+m.checking = Buscando actualizaciones
+m.validating = Validando
+m.patching = Parchando
+m.launching = Lanzando
+
+m.patch_notes = Notas del parche
+
+m.complete = {0}% completado
+m.remain = {0} restante
+
+m.updating_metadata = Descargando los archivos de control
+
+m.init_failed = Un archivo de configuraci\u00f3n est\u00e1 faltante o est\u00e1 corrupto. Intentando \
+  descargar una nueva copia...
+
+m.java_download_failed = No ha sido posible descargar autom\u00e1ticamente la \
+  versi\u00f3n de Java necesaria para tu computadora.\n\n\
+  Por favor ve a www.java.com y descarga la \u00faltima versi\u00f3n de \
+  Java, despu\u00e9s intenta correr de nuevo la aplicaci\u00f3n.
+
+m.java_unpack_failed = No ha sido posible desempacar una versi\u00f3n actualizada de \
+  Java. Por favor aseg\u00farate de tener al menos 100 MB de espacio libre en tu \
+  disco duro e intenta correr de nuevo la aplicaci\u00f3n.\n\n\
+  Si eso no soluciona el problema, ve a www.java.com y descarga e \
+  instala la \u00faltima versi\u00f3n de Java e intenta de nuevo.
+
+m.unable_to_repair = No ha sido posible descargar los archivos necesarios despu\u00e9s de \
+  cinco intentos. Puedes intentar correr de nuevo la aplicaci\u00f3n, pero si falla \
+  de nuevo podr\u00edas necesitar desinstalar y reinstalar.
+
+m.unknown_error = La aplicaci\u00f3n no ha podido iniciar debido a un extra\u00f1o \
+  error del que no se pudo recobrar. Por favor visita\n{0} para ver informaci\u00f3n acerca \
+  de como recuperarla.
+m.init_error = La aplicaci\u00f3n no ha podido iniciar debido al siguiente \
+  error:\n{0}\n\nPor favor visita\n{1} para \
+  ver informaci\u00f3n acerca de como manejar ese tipo de problemas.
+
+m.readonly_error = El directorio en el que esta aplicaci\u00f3n est\u00e1 instalada: \
+  \n{0}\nes solo lectura. Por favor instala la aplicaci\u00f3n en un directorio en el cual \
+  tengas acceso de escritura.
+
+m.missing_resource = La aplicaci\u00f3n no ha podido iniciar debido a un recurso \
+  faltante:\n{0}\n\nPor favor visita\n{1} para informaci\u00f3n acerca de como solucionar \
+  estos problemas.
+
+m.insufficient_permissions_error = No aceptaste la firma digital de \
+ esta aplicaci\u00f3n. Si quieres correr la aplicaci\u00f3n, necesitas aceptar \
+ su firma digital.\n\nPara hacerlo, necesitas cerrar tu navegador, \
+ reiniciarlo, y regresar a esta p\u00e1gina web para reiniciar la aplicaci\u00f3n. Cuando se muestre \
+ el di\u00e1logo de seguridad, haz clic en el bot\u00f3n para aceptar la firmar digital \
+ y otorgar a esta aplicaci\u00f3n los privilegios que necesita para correr.
+
+m.corrupt_digest_signature_error = No pudimos verificar la firma digital \
+ de la aplicaci\u00f3n.\nPor favor revisa que est\u00e9s lanzando la aplicaci\u00f3n desde\nel \
+ sitio web correcto.
+
+m.default_install_error = la secci\u00f3n de asistencia de este sitio web
+
+m.another_getdown_running = Est\u00e1n corriendo m\u00faltiples instancias de \
+ este instalador.  Este se detendr\u00e1 para permitir que otra contin\u00fae.
+
+m.applet_stopped = Se le dijo al applet de Getdown que dejara de trabajar.
+
+# application/digest errors
+m.missing_appbase = Al archivo de configuraci\u00f3n le falta el 'appbase'.
+m.invalid_version = El archivo de configuraci\u00f3n especifica una versi\u00f3n no v\u00e1lida.
+m.invalid_appbase = El archivo de configuraci\u00f3n especifica un 'appbase' no v\u00e1lido.
+m.missing_class = Al archivo de configuraci\u00f3n le falta la clase de aplicaci\u00f3n.
+m.missing_code = El archivo de configuraci\u00f3n especifica que no hay recursos de c\u00f3digo.
+m.invalid_digest_file = El archivo digest no es v\u00e1lido.
+
+
+# When at 0%
+m.initialising = Initialising
+m.starting = Starting
diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties
new file mode 100644 (file)
index 0000000..477830e
--- /dev/null
@@ -0,0 +1,115 @@
+#
+# $Id: messages.properties 485 2012-03-08 22:05:30Z ray.j.greenwell $
+#
+# Getdown translation messages
+
+m.abort_title = Annuler l'installation?
+m.abort_confirm =<html>\u00cates-vous s\u00fbr de vouloir annuler l'installation? \
+   Vous pourrez reprendre l'installation en ex\u00e9cutant l'application de nouveau.</html>
+m.abort_ok = Quitter
+m.abort_cancel = Continuer l'installation
+
+m.detecting_proxy = D\u00e9tection automatique des r\u00e9glages proxy
+
+m.configure_proxy =<html>Connexion au serveur impossible. \
+   <ul><li>  Veuillez v\u00e9rifier que <code>javaw.exe</code> n'est bloqu\u00e9 \
+   par aucun pare-feu ou antivirus. \
+   Vous pouvez vous rendre sur la configuration du pare-feu windows via \
+   (D\u00e9marrer ->  Panneau de Configuration ->  Pare-feu Windows ).</ul> \
+   <p>  Il est \u00e9galement possible que vous soyez derri\u00e8re un proxy que l'application \
+   est incapable de d\u00e9tecter automatiquement. \
+   Si tel est le cas, veuillez saisir les r\u00e9glages proxy ci-dessous.</html>
+
+m.proxy_extra =<html>Si vous \u00eates certain de ne pas utiliser de proxy, il est \
+   possible qu'une interruption temporaire de la connexion internet emp\u00fbche la \
+   communication avec les serveurs. Dans ce cas, vous pouvez relancer \
+   l'installation ult\u00e9rieurement.</html>
+
+m.proxy_host = Proxy IP
+m.proxy_port = Proxy port
+m.proxy_username = Nom d'utilisateur
+m.proxy_password = Mot de passe
+m.proxy_auth_required = Identification requise
+m.proxy_ok = OK
+m.proxy_cancel = Annuler
+
+m.downloading_java = T\u00e9l\u00e9chargement en cours de la Machine Virtuelle Java
+m.unpacking_java = D\u00e9compression en cours de la Machine Virtuelle Java
+
+m.resolving = R\u00e9solution des t\u00e9l\u00e9chargements en cours
+m.downloading = T\u00e9l\u00e9chargement des donn\u00e9es en cours
+m.failure = \u00c9chec du t\u00e9l\u00e9chargement: {0}
+
+m.checking = V\u00e9rification de la mise-\u00e0-jour en cours
+m.validating = Validation en cours
+m.patching = Modification en cours
+m.launching = Lancement en cours
+
+m.patch_notes = Notes de mise-\u00e0-jour
+
+m.complete = Complet \u00e0 {0}%
+m.remain = {0} restant
+
+m.updating_metadata = T\u00e9l\u00e9chargement des fichiers de contr\u00f4les en cours
+
+m.init_failed = Notre fichier de configuration est perdu ou corrompu. T\u00e9l\u00e9chargement \
+   d'une nouvelle copie en cours ...
+
+m.java_download_failed = Impossible de t\u00e9l\u00e9charger automatiquement la \
+   version de Java n\u00e9cessaire.\n\n\
+   Veuillez vous rendre sur www.java.com et t\u00e9l\u00e9charger et installer la version \
+   la plus r\u00e9cente de Java, avant d'ex\u00e9cuter l'application \u00e0 nouveau.
+
+m.java_unpack_failed = Impossible de d\u00e9compresser la version de \
+   Java n\u00e9cessaire. Veuillez v\u00e9rifier que vous avez au moins 100 MB d'espace libre \
+   sur votre disque dur puis tenter d'ex\u00e9cuter l'application \u00e0 nouveau.\n\n\
+   Si le probl\u00e8me persiste, rendez vous www.java.com et t\u00e9l\u00e9chargez et \
+   installez la version plus r\u00e9cente de Java puis essayez de nouveau.
+
+m.unable_to_repair = Impossible de t\u00e9l\u00e9charger les fichiers n\u00e9cessaires apr\u00e8s \
+   cinq tentatives. Vous pouvez tenter d'ex\u00e9cuter l'application \u00e0 nouveau, mais il est \
+   possible qu'une d\u00e9sinstallation / r\u00e9installation soit n\u00e9cessaire.
+
+m.unknown_error = Une erreur inconnue a fait \u00e9chouer le lancement de l'application. \
+   Veuillez visiter\n{0} pour plus d'informations.
+m.init_error = Le lancement de l'application a \u00e9chou\u00e9 \u00e0 cause de l'erreur \
+   suivante:\n{0}\n\nVeuillez visiter\n{1} pour plus d'informations.
+
+m.readonly_error = Le r\u00e9pertoire d'installation de cette application: \
+   \n{0}\nest en lecture seule. Veuillez installer l'application dans un r\u00e9pertoire avec \
+   un acc\u00e8s en \u00e9criture.
+
+m.missing_resource = Le lancement de l'application a \u00e9chou\u00e9 \u00e0 cause d'une \
+   ressource manquante:\n{0}\n\nVeuillez visiter\n{1} pour plus d'informations.
+
+m.insufficient_permissions_error = Vous n'avez pas accepter la signature \
+  num\u00e9rique de cette application. Si vous souhaitez ex\u00e9cuter cette application, vous \
+  devez accepter sa signature num\u00e9rique.\n\nAfin de le faire, vous devez quitter votre \
+  navigateur, le red\u00e9marrer, retourner \u00e0 cette page puis relancer l'application. \
+  Une fois la bo\u00eete de dialogue de s\u00e9curit\u00e9 affich\u00e9e, cliquez sur le bouton \
+  pour accepter la signature num\u00e9rique et accorder les permissions n\u00e9cessaires au bon \
+  fonctionnement de l'application.
+
+m.corrupt_digest_signature_error = Nous ne pouvons pas v\u00e9rifier la signature num\u00e9rique \
+  de l'application.\nVeuillez v\u00e9rifier que vous lancez l'application \ndepuis \
+  la bonne adresse internet.
+
+m.default_install_error = la section de support du site
+
+m.another_getdown_running = Plusieurs instances d'installation de cette \
+  application sont d\u00e9j\u00e0 en cours d'ex\u00e9cution.  Cette instance va s'arr\u00eater \
+  afin de permettre aux autres d'aboutir.
+
+m.applet_stopped = L'appelet Getdown a \u00e9t\u00e9 stopp\u00e9e.
+
+# application/digest errors
+m.missing_appbase = Le fichier de configuration ne contient pas 'appbase'.
+m.invalid_version = Le fichier de configuration sp\u00e9cifie une version invalide.
+m.invalid_appbase = Le fichier de configuration sp\u00e9cifie un 'appbase' invalide.
+m.missing_class = Le fichier de configuration ne contient pas la classe de l'application.
+m.missing_code = Le fichier de configuration ne sp\u00e9cifie aucune ressource de code.
+m.invalid_digest_file = Le fichier digest est invalide.
+
+# When at 0%
+m.initialising = Initialising
+m.starting = Starting
diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_it.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_it.properties
new file mode 100644 (file)
index 0000000..d5e3659
--- /dev/null
@@ -0,0 +1,118 @@
+#
+# $Id$
+#
+# Getdown translation messages
+
+m.abort_title = Annullare l'installazione?
+m.abort_confirm = <html>Sei sicuro di voler annullare l'installazione? \
+  Potrai riprenderla in seguito, riavviando nuovamente l'applicazione.</html>
+m.abort_ok = Chiudi
+m.abort_cancel = Continua l'installazione
+
+m.detecting_proxy = Provo a recuperare le configurazioni del proxy
+
+m.configure_proxy = <html>Impossibile collegarsi al server per \
+  recuperare i dati. \
+  <ul><li> Se il Firewall di Windows o Norton Internet Security bloccano \
+  <code>javaw.exe</code> non si possono scaricare i dati. Devi \
+  permettere a <code>javaw.exe</code> di accedere a internet. Puoi provare \
+  di nuovo, ma dovresti abilitare javaw.exe nella tua configurazione \
+  del firewall ( Start -> Pannello di Controllo -> Windows Firewall ).</ul> \
+  <p> Il tuo computer potrebbe accedere a internet attraverso un proxy e \
+  questo potrebbe non essere stato riconosciuto automaticamente. Se conosci le \
+  tue impostazioni del proxy, puoi inserirle di seguito.</html>
+
+m.proxy_extra = <html>Se sei sicuro di non usare proxy  \
+  potrebbe essere un problema di internet o di collegamento con il server. \
+  In questo caso puoi annullare e ripetere l'installazione più tardi.</html>
+
+m.proxy_host = IP Proxy
+m.proxy_port = Porta Proxy
+m.proxy_username = Nome utente
+m.proxy_password = Parola d'ordine
+m.proxy_auth_required = Autenticazione richiesta
+m.proxy_ok = OK
+m.proxy_cancel = Annulla
+
+m.downloading_java = Scaricando la Java Virtual Machine
+m.unpacking_java = Scompattando la Java Virtual Machine
+
+m.resolving = Recuperando i file da scaricare
+m.downloading = Download dei dati
+m.failure = Download fallito: {0}
+
+m.checking = Sto controllando gli aggiornamenti
+m.validating = Validazione
+m.patching = Applico le patch
+m.launching = Avvio
+
+m.patch_notes = Note delle Patch
+m.play_again = Avvia Nuovamente
+
+m.complete = {0}% completato
+m.remain = {0} rimasto
+
+m.updating_metadata = Scarico i file di controllo
+
+m.init_failed = La configurazione Ã¨ corrotta o mancante. Provo a \
+  scaricarne una nuova copia...
+
+m.java_download_failed = Impossibile scaricare la versione corretta \
+  di Java per il tuo computer.\n\n\
+  Visita www.java.com e scarica l'ultima versione di \
+  Java, poi lancia di nuovo l'applicazione.
+
+m.java_unpack_failed = Impossibile scompattare l'aggiornamento di \
+  Java. Verifica di avere almeno 100 MB di spazio libero nel tuo \
+  hard disk e prova a rilanciare l'applicazione.\n\n\
+  Se l'errore persiste, vistia www.java.com, scarica e \
+  installa l'ultima versione di Java e riprova.
+
+m.unable_to_repair = Impossibile scaricare i file necessari dopo 5 \
+  tentativi. Puoi provare a rilanciare l'applicazione, ma se fallisce \
+  di nuovo potresti dover reinstallarla.
+
+m.unknown_error = L'applicazione non Ã¨ stata avviata a causa di uno strano \
+  errore che non conosco. Visita\n{0} per avere informazioni \
+  in merito.
+m.init_error = L'applicazione non Ã¨ stata avviata a causa del seguente \
+  errore:\n{0}\n\nVistita\n{1} per avere \
+  informazioni su come risolvere il problema.
+
+m.readonly_error = La directory dove l'applicazione Ã¨ installata: \
+  \n{0}\nè in sola lettura. Installa l'applicazione dove hai i diritti \
+  di scrittura.
+
+m.missing_resource = L'applicazione non Ã¨ stata avviata a causa di mancanza \
+  di risorse:\n{0}\n\nVisita\n{1} per avere informazioni su come risolvere \
+  questi problemi.
+
+m.insufficient_permissions_error = Non hai accettato la \
+ firma digitale. Se vuoi eseguire l'applicazione devi accettare la \
+ firma digitale.\n\nPer farlo, riavvia il tuo browser \
+ e ritorna in questa pagina per rilanciare l'applicazione. Quando l'avviso \
+ di sicurezza viene mostrato, clicca per accettare la firma digitale \
+ ed eseguire l'applicazione con i privilegi necessari.
+
+m.corrupt_digest_signature_error = Impossibile verificare la firma digitale dell'applicazione \
+ .\nControlla di aver lanciato l'applicazione dal\n\
+ sito web corretto.
+
+m.default_install_error = la sezione di supporto del sito
+
+m.another_getdown_running = E' già in esecuzione un'istanza del programma. \
+ Questa verrà chiusa.
+
+m.applet_stopped = L'applet di Getdown Ã¨ stata interrotta.
+
+# application/digest errors
+m.missing_appbase = Il tag "appbase" Ã¨ mancante.
+m.invalid_version = Il file di configurazione non contiene una versione valida (tag "version").
+m.invalid_appbase = Il tag "appbase" non Ã¨ valido.
+m.missing_class = Il file di configurazione non contiene la classe da eseguire (tag "class").
+m.missing_code = Il file di configurazione non contiene alcuna risorsa (tag "code").
+m.invalid_digest_file = Il file di digest non Ã¨ valido.
+
+# When at 0%
+m.initialising = Initialising
+m.starting = Starting
diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties
new file mode 100644 (file)
index 0000000..7eabcdc
--- /dev/null
@@ -0,0 +1,111 @@
+#
+# $Id$
+#
+# Getdown translation messages
+
+m.abort_title = \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u4e2d\u6b62\u3057\u307e\u3059\u304b\uff1f 
+m.abort_confirm = <html>\u672c\u5f53\u306b\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u4e2d\u6b62\u3057\u307e\u3059\u304b\uff1f  \
+  \u5f8c\u3067\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u305f\u969b\u306b\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u518d\u958b\u3067\u304d\u307e\u3059\u3002</html> 
+m.abort_ok = \u4e2d\u6b62 
+m.abort_cancel = \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306e\u7d9a\u884c 
+
+m.detecting_proxy = \u81ea\u52d5\u30d7\u30ed\u30ad\u30b7\u8a2d\u5b9a\u5b9f\u884c\u4e2d
+
+m.configure_proxy = <html>\u30b5\u30fc\u30d0\u306b\u63a5\u7d9a\u3067\u304d\u306a\u3044\u305f\u3081\u3001\u30b2\u30fc\u30e0\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u306b \
+  \u5931\u6557\u3057\u307e\u3057\u305f\u3002  \
+  <ul><li>\u30a6\u30a3\u30f3\u30c9\u30a6\u30ba\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u307e\u305f\u306f\u30ce\u30fc\u30c8\u30f3\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u304c \
+  <code>javaw.exe</code>\u3092\u30d6\u30ed\u30c3\u30af\u3059\u308b\u3088\u3046\u8a2d\u5b9a\u3057\u3066\u3042\u308b\u5834\u5408\u306f\u3001\u30b2\u30fc\u30e0\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093\u3002  \u8a2d\u5b9a\u3092 \
+  <code>javaw.exe</code>\u7d4c\u7531\u3067\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u3088\u3046\u306b\u5909\u66f4\u3057\u3066\u304f\u3060\u3055\u3044\u3002  \u30b2\u30fc\u30e0\u3092\u518d\u8d77\u52d5 \
+  \u3057\u305f\u5f8c\u3001\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u306e\u8a2d\u5b9a\u304b\u3089javaw.exe \u3092\u524a\u9664 \
+  \u3057\u3066\u304f\u3060\u3055\u3044\uff08\u30b9\u30bf\u30fc\u30c8\u2192\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u30d1\u30cd\u30eb\u2192\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\uff09\u3002</ul> \
+  <p>\u30d7\u30ed\u30ad\u30b7\u8a2d\u5b9a\u306e\u81ea\u52d5\u691c\u51fa\u304c\u3067\u304d\u307e\u305b\u3093\u3002\u304a\u4f7f\u3044\u306e\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u30fc\u306f \
+  \u30d7\u30ed\u30ad\u30b7\u3092\u4f7f\u7528\u3057\u3066\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u3078\u30a2\u30af\u30bb\u30b9\u3057\u3066\u3044\u307e\u3059\u3002  \u30d7\u30ed\u30ad\u30b7\u8a2d\u5b9a\u306e\u8a73\u7d30\u304c \
+  \u308f\u304b\u3063\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u4e0b\u306b\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002</html> 
+
+m.proxy_extra = <html>\u30d7\u30ed\u30ad\u30b7\u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001\u4e00\u6642\u7684\u306a \
+  \u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u306e\u4e0d\u5177\u5408\u306b\u3088\u308a\u3001\u30b5\u30fc\u30d0\u3068\u4ea4\u4fe1\u3067\u304d\u306a\u3044\u72b6\u614b\u306b\u3042\u308b \
+  \u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002  \u305d\u306e\u5834\u5408\u306f\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u3057\u3066\u3001 \
+  \u5f8c\u307b\u3069\u6539\u3081\u3066\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002</html> 
+
+m.proxy_host = \u30d7\u30ed\u30ad\u30b7IP 
+m.proxy_port = \u30d7\u30ed\u30ad\u30b7\u30dd\u30fc\u30c8
+m.proxy_username = Username
+m.proxy_password = Password
+m.proxy_auth_required = Authentication required
+m.proxy_ok = OK  
+m.proxy_cancel = \u30ad\u30e3\u30f3\u30bb\u30eb 
+
+m.downloading_java = Java\u30d0\u30fc\u30c1\u30e3\u30eb\u30de\u30b7\u30f3\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d
+m.unpacking_java = Java\u30d0\u30fc\u30c1\u30e3\u30eb\u30de\u30b7\u30f3\u306e\u89e3\u51cd\u4e2d
+
+m.resolving = \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u306e\u8a2d\u5b9a\u4e2d
+m.downloading = \u30c7\u30fc\u30bf\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d
+m.failure = \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u5931\u6557\uff1a  {0} 
+
+m.checking = \u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u306e\u78ba\u8a8d\u4e2d
+m.validating = \u8a8d\u8a3c\u4e2d
+m.patching = \u4fee\u6b63\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u5b9f\u884c\u4e2d
+m.launching = \u5b9f\u884c\u4e2d
+
+m.complete = {0}\uff05\u5b8c\u4e86 
+m.remain = \u3000\u6b8b\u308a{0} 
+
+m.updating_metadata = \u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u30d5\u30a1\u30a4\u30eb\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d
+
+m.init_failed = \u74b0\u5883\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u306a\u3044\u304b\u3001\u307e\u305f\u306f\u58ca\u308c\u3066\u3044\u307e\u3059\u3002  \u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092 \
+  \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d\u2026 
+
+m.java_download_failed = \u304a\u4f7f\u3044\u306e\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u30fc\u306b\u3001Java\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u6700\u65b0 \
+  \u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u81ea\u52d5\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n\n \
+  www.java.com \u304b\u3089\u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u624b\u52d5\u3067\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3057\u3066\u3001 \
+  \u518d\u5ea6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002 
+
+m.java_unpack_failed = Java\u306e\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u89e3\u51cd \
+  \u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002  \u30cf\u30fc\u30c9\u30c9\u30e9\u30a4\u30d6\u306e\u30e1\u30e2\u30ea\u304c100MB\u4ee5\u4e0a\u3042\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u304b\u3089 \
+  \u518d\u5ea6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n \
+  \u554f\u984c\u304c\u89e3\u6c7a\u3057\u306a\u3044\u5834\u5408\u306f\u3001www.java.com \u304b\u3089Java\u306e\u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092 \
+  \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3057\u3066\u304b\u3089\u3001\u518d\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 
+
+m.unable_to_repair = 5\u56de\u8a66\u884c\u3057\u307e\u3057\u305f\u304c\u3001\u5fc5\u8981\u306a\u30d5\u30a1\u30a4\u30eb\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9 \
+  \u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002  \u5f8c\u307b\u3069\u6539\u3081\u3066\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 \
+  \u518d\u5ea6\u5931\u6557\u3057\u305f\u5834\u5408\u306f\u3001\u30a2\u30f3\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u5f8c\u306b\u518d\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u3066\u304f\u3060\u3055\u3044\u3002 
+
+m.unknown_error = \u539f\u56e0\u4e0d\u660e\u306e\u30a8\u30e9\u30fc\u306b\u3088\u308a\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c \
+  \u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002  \u89e3\u6c7a\u65b9\u6cd5\u3092\n{0}\u3067 \
+  \u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 
+m.init_error = \u6b21\u306e\u30a8\u30e9\u30fc\u306b\u3088\u308a\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093 \
+  \u3067\u3057\u305f\u3002\n{0}\n\n\u5bfe\u51e6\u65b9\u6cd5\u3092\n{1}\u3067 \
+  \u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 
+
+m.readonly_error = \u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3055\u308c\u305f\u30d5\u30a9\u30eb\u30c0\u306f  \
+  \n{0}\n\u8aad\u307f\u53d6\u308a\u5c02\u7528\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002  \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u66f8\u304d\u8fbc\u307f\u304c\u3067\u304d\u308b\u30d5\u30a9\u30eb\u30c0\u306b \
+  \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u3066\u304f\u3060\u3055\u3044\u3002 
+
+m.missing_resource = \u30ea\u30bd\u30fc\u30b9\u4e0d\u660e\u306e\u305f\u3081\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093 \
+  \u3067\u3057\u305f\u3002\n{0}\n\n\u5bfe\u51e6\u65b9\u6cd5\u3092\n{1}\u3067\u78ba\u8a8d \
+  \u3057\u3066\u304f\u3060\u3055\u3044\u3002 
+
+m.insufficient_permissions_error = \u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30c7\u30b8\u30bf\u30eb\u7f72\u540d\u304c\u62d2\u5426 \
+ \u3055\u308c\u307e\u3057\u305f\u3002  \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3059\u308b\u5834\u5408\u306f\u3001\u30c7\u30b8\u30bf\u30eb\u7f72\u540d\u306e\u627f\u8a8d\u304c \
+ \u5fc5\u8981\u3067\u3059\u3002\n\n\u627f\u8a8d\u306b\u306f\u3001\u30d6\u30e9\u30a6\u30b6\u3092\u9589\u3058\u3066\u304b\u3089\u518d\u5ea6\u958b\u304d\u3001 \
+ \u672c\u30db\u30fc\u30e0\u30da\u30fc\u30b8\u3092\u518d\u8868\u793a\u3057\u3066\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u518d\u5ea6\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044  \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u306e \
+ \u8b66\u544a\u304c\u8868\u793a\u3055\u308c\u305f\u6642\u306f\u3001\u5b9f\u884c\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30c7\u30b8\u30bf\u30eb\u7f72\u540d\u3092\u627f\u8a8d\u3057\u3001 \
+ \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 
+
+m.corrupt_digest_signature_error = \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30c7\u30b8\u30bf\u30eb\u7f72\u540d\u304c\u8a8d\u8a3c \
+ \u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n\u6307\u5b9a\u30db\u30fc\u30e0\u30da\u30fc\u30b8\u304b\u3089\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3057\u3066\u3044\u308b\u304b\n \
+ \u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 
+
+m.default_install_error = \u30db\u30fc\u30e0\u30da\u30fc\u30b8\u3067\u306e\u30b5\u30dd\u30fc\u30c8\u8868\u793a 
+
+# application/digest errors
+m.missing_appbase = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306eappbase\u304c\u4e0d\u660e\u3067\u3059\u3002 
+m.invalid_version = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306f\u7121\u52b9\u306a\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u6307\u5b9a\u3057\u3066\u3044\u307e\u3059\u3002 
+m.invalid_appbase = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u304c\u7121\u52b9\u306aappbase\u3092\u6307\u5b9a\u3057\u3066\u3044\u307e\u3059\u3002 
+m.missing_class = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30af\u30e9\u30b9\u304c\u4e0d\u660e\u3067\u3059\u3002 
+m.missing_code = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3067\u30b3\u30fc\u30c9\u30ea\u30bd\u30fc\u30b9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 
+m.invalid_digest_file = \u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb\u304c\u7121\u52b9\u3067\u3059\u3002 
+
+# When at 0%
+m.initialising = Initialising
+m.starting = Starting
diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties
new file mode 100644 (file)
index 0000000..dbb45f4
--- /dev/null
@@ -0,0 +1,106 @@
+#
+# $Id$
+#
+# Getdown translation messages
+
+m.abort_title = \uC124\uCE58\uB97C \uCDE8\uC18C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?
+m.abort_confirm = <html>\uC815\uB9D0\uB85C \uC124\uCE58\uB97C \uCDE8\uC18C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? \
+  \uB098\uC911\uC5D0 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uC2E4\uD589\uD558\uC5EC \uC124\uCE58\uB97C \uC7AC\uAC1C\uD558\uC5EC \uC8FC\uC2ED\uC2DC\uC624.</html>
+m.abort_ok = \uC911\uC9C0
+m.abort_cancel = \uACC4\uC18D\uD558\uC5EC \uC124\uCE58
+
+m.detecting_proxy = \uC790\uB3D9 \uD504\uB85D\uC2DC\uB97C \uC124\uC815\uC744 \uC2DC\uB3C4
+
+m.configure_proxy = <html>\uAC8C\uC784 \uB370\uC774\uD130\uB97C \uBC1B\uAE30 \uC704\uD55C \uC11C\uBC84 \uC811\uC18D\uC5D0 \uC2E4\uD328\uD558\uC600\uC2B5\uB2C8\uB2E4.\
+  <ul><li>\uC708\uB3C4\uC6B0 \uBC29\uD654\uBCBD \uB610\uB294 \uB178\uD134 \uC778\uD130\uB137 \uC2DC\uD050\uB9AC\uD2F0\uAC00 <code>javaw.exe</code>\uC774 \uC124\uC815\uC5D0\uC11C \uCC28\uB2E8\uB418\uC5B4 \uC788\uC744 \uACBD\uC6B0, \
+  \uAC8C\uC784 \uB370\uC774\uD130\uB97C \uB2E4\uC6B4\uB85C\uB4DC \uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \
+  <code>javaw.exe</code>\uAC00 \uC778\uD130\uB137 \uC5F0\uACB0\uC744 \uD560 \uC218 \uC788\uB3C4\uB85D \uC124\uC815\uC744 \uBCC0\uACBD\uD558\uC5EC \uC8FC\uC2ED\uC2DC\uC624. \
+  \uAC8C\uC784\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD55C \uD6C4, \uBC29\uD654\uBCBD \uC124\uC815\uC5D0\uC11C javaw.exe\uB97C \uC0AD\uC81C\uD558\uC5EC \uC8FC\uC2ED\uC2DC\uC624. \
+  ( \uC2DC\uC791 -> \uC81C\uC5B4\uD310 -> \uC708\uB3C4\uC6B0 \uBC29\uD654\uBCBD )</ul> \
+  <p> \uCEF4\uD4E8\uD130\uAC00 \uD504\uB85D\uC2DC \uC11C\uBC84\uB97C \uD1B5\uD574 \uC778\uD130\uB137\uC5D0 \uC5F0\uACB0\uB418\uC5B4 \uC788\uB2E4\uBA74, \uD504\uB85D\uC2DC \uC124\uC815\uC758 \uC790\uB3D9 \uAD6C\uC131\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC73C\uBBC0\uB85C, \
+  \uC0AC\uC6A9\uD558\uB294 \uD504\uB85D\uC2DC \uC124\uC815\uC744 \uC54C\uACE0 \uC788\uC744 \uACBD\uC6B0 \uC544\uB798\uC5D0 \uC785\uB825\uD558\uC5EC \uC8FC\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4.</html>
+
+m.proxy_extra = \uC790\uB3D9 \uD504\uB85D\uC2DC\uB97C \uC124\uC815\uC744 \uC2DC\uB3C4
+
+m.proxy_host = \uD504\uB85D\uC2DC IP
+m.proxy_port = \uD504\uB85D\uC2DC \uD3EC\uD2B8
+m.proxy_username = Username
+m.proxy_password = Password
+m.proxy_auth_required = Authentication required
+m.proxy_ok = OK
+m.proxy_cancel = \uCDE8\uC18C
+
+m.downloading_java = \uC790\uBC14 \uAC00\uC0C1 \uBA38\uC2E0(JVM) \uB2E4\uC6B4\uB85C\uB4DC \uC911
+m.unpacking_java = \uC790\uBC14 \uAC00\uC0C1 \uBA38\uC2E0(JVM) \uC555\uCD95\uC744 \uD574\uC81C\uD558\uB294 \uC911
+
+m.resolving = \uB2E4\uC6B4\uB85C\uB4DC \uBD84\uC11D \uC911
+m.downloading = \uB370\uC774\uD130 \uB2E4\uC6B4\uB85C\uB4DC \uC911
+m.failure = \uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328: {0}
+
+m.checking = \uC5C5\uB370\uC774\uD2B8 \uCCB4\uD06C
+m.validating = \uC720\uD6A8\uC131 \uAC80\uC0AC \uC911
+m.patching = \uD328\uCE58 \uC911
+m.launching = \uC2E4\uD589 \uC911
+
+m.patch_notes = \uD328\uCE58 \uB178\uD2B8
+m.play_again = \uB2E4\uC2DC \uC2E4\uD589
+
+m.complete = {0}% \uC644\uB8CC
+m.remain = {0} \uB0A8\uC74C
+
+m.updating_metadata = \uCEE8\uD2B8\uB864 \uD30C\uC77C\uC744 \uB2E4\uC6B4\uB85C\uB4DC \uC911
+
+m.init_failed = \uC124\uC815 \uD30C\uC77C\uC774 \uB204\uB77D\uB418\uC5C8\uAC70\uB098 \uBCC0\uD615\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \
+  \uC0C8\uB85C\uC6B4 \uBCF5\uC0AC\uBCF8\uC744 \uB2E4\uC6B4\uB85C\uB4DC \uC911\uC785\uB2C8\uB2E4...
+
+m.java_download_failed = \uC774 \uCEF4\uD4E8\uD130\uC5D0 \uD544\uC694\uD55C \uC0C8\uB85C\uC6B4 \uBC84\uC804\uC758 \uC790\uBC14\uB97C \uC790\uB3D9\uC73C\uB85C \uB2E4\uC6B4\uB85C\uB4DC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n\n\
+  \uC790\uBC14 \uC6F9\uC0AC\uC774\uD2B8(www.java.com)\uB85C \uAC00\uC11C \uCD5C\uC2E0\uC758 \uC790\uBC14\uB97C \uB2E4\uC6B4\uB85C\uB4DC \uBC1B\uC73C\uC2E0 \uD6C4, \
+  \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC2ED\uC2DC\uC624.
+
+m.java_unpack_failed = \uC5C5\uB370\uC774\uD2B8\uB41C \uBC84\uC804\uC758 \uC790\uBC14\uC758 \uC555\uCD95\uC744 \uD480 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \
+  \uD558\uB4DC\uB4DC\uB77C\uC774\uBE0C\uC5D0 \uCD5C\uC18C\uD55C 100MB\uC758 \uC6A9\uB7C9\uC744 \uD655\uBCF4\uD55C \uC774\uD6C4, \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC2ED\uC2DC\uC624.\n\n\
+  \uB9CC\uC57D \uBB38\uC81C\uAC00 \uD574\uACB0\uB418\uC9C0 \uC54A\uB294\uB2E4\uBA74, \uC790\uBC14 \uC6F9\uC0AC\uC774\uD2B8(www.java.com)\uB85C \uAC00\uC11C \uCD5C\uC2E0\uC758 \uC790\uBC14\uB97C \uB2E4\uC6B4\uB85C\uB4DC \uBC1B\uC73C\uC2E0 \uD6C4, \
+  \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC2ED\uC2DC\uC624.
+
+m.unable_to_repair = \uB2E4\uC12F\uBC88\uC758 \uC2DC\uB3C4\uC5D0\uB3C4 \uD544\uC694\uD55C \uD30C\uC77C\uC744 \uB2E4\uC6B4\uB85C\uB4DC\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \
+  \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uB2E4\uC2DC \uC2DC\uC791\uD574\uBCF4\uC2DC\uACE0, \uADF8\uB798\uB3C4 \uB2E4\uC6B4\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD55C\uB2E4\uBA74, \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uC81C\uAC70\uD55C \uD6C4, \uB2E4\uC2DC \uC2E4\uD589\uD574\uBCF4\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4.
+
+m.unknown_error = \uBCF5\uAD6C\uB420 \uC218 \uC5C6\uB294 \uC624\uB958\uB85C \uC778\uD558\uC5EC \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC758 \uC2E4\uD589\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \
+  \n{0}\uC5D0 \uB300\uD55C \uBCF5\uAD6C \uBC29\uBC95\uC744 \uCC3E\uAE30 \uC704\uD574\uC11C \uBC29\uBB38\uD558\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4.
+
+m.init_error = \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC774 \uC544\uB798\uC640 \uAC19\uC740 \uC5D0\uB7EC\uB85C \uC2E4\uD589\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC5D0\uB7EC:\
+  \n{0}\n\n{1}\uC5D0 \uB300\uD55C \uBB38\uC81C \uD574\uACB0 \uBC29\uBC95\uC744 \uCC3E\uAE30 \uC704\uD574\uC11C \uBC29\uBB38\uD558\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4.
+
+m.readonly_error = \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC774 \uC124\uCE58\uB41C \uB514\uB809\uD1A0\uB9AC: \
+  \n{0}\n\uAC00 \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4. \uC77D\uAE30 \uAD8C\uD55C\uC774 \uC2B9\uC778\uB41C \uB809\uD1A0\uB9AC\uC5D0 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uC124\uCE58\uD558\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4.
+
+m.missing_resource = \uB9AC\uC18C\uC2A4\uC758 \uC190\uC2E4\uB85C \uC778\uD558\uC5EC \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC758 \uC2E4\uD589\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. : \
+  \n{0}\n\n{1}\uC5D0 \uB300\uD55C \uBB38\uC81C \uD574\uACB0 \uBC29\uBC95\uC744 \uCC3E\uAE30 \uC704\uD574\uC11C \uBC29\uBB38\uD558\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4.
+
+m.insufficient_permissions_error = \uC774 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC758 \uB514\uC9C0\uD0C8 \uC11C\uBA85\uC744 \uD655\uC778\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \
+ \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uC2E4\uD589\uD558\uAE30 \uC704\uD574\uC11C \uB514\uC9C0\uD0C8 \uC11C\uBA85\uC744 \uD655\uC778\uD558\uC5EC \uC8FC\uC2ED\uC2DC\uC624. \
+ \n\n\uADF8\uB9AC\uACE0 \uB098\uC11C \uC6F9 \uBE0C\uB77C\uC6B0\uC800\uB97C \uB2EB\uACE0 \uB2E4\uC2DC \uC2DC\uC791\uD558\uC5EC \uC6F9\uD398\uC774\uC9C0\uB85C \uB3CC\uC544\uC640 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uC7AC\uC2DC\uC791\uD574\uC8FC\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4. \
+ \uBCF4\uC548\uC5D0 \uB300\uD55C \uB300\uD654\uC0C1\uC790\uAC00 \uBCF4\uC774\uBA74, \uB514\uC9C0\uD0C8 \uC11C\uBA85\uC5D0 \uB300\uD55C \uD655\uC778 \uBC84\uD2BC\uC744 \uD074\uB9AD\uD558\uACE0, \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC774 \uC2E4\uD589\uB418\uAE30 \uC704\uD55C \
+ \uAD8C\uD55C\uC744 \uBD80\uC5EC\uD574\uC8FC\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4.
+
+m.corrupt_digest_signature_error = \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC758 \uB514\uC9C0\uD0C8 \uC11C\uBA85\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n \
+ \uC62C\uBC14\uB978 \uC6F9\uC0AC\uC774\uD2B8\uC5D0\uC11C \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC774 \uC2E4\uD589\uB418\uACE0 \uC788\uB294 \uC9C0 \uD655\uC778\uBC14\uB78D\uB2C8\uB2E4.
+
+m.default_install_error = \uC6F9\uC0AC\uC774\uD2B8\uC758 \uC9C0\uC6D0 \uBA54\uB274(support section)\uB97C \uD655\uC778\uD558\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4.
+
+m.another_getdown_running = \uC774 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158 \uC778\uC2A4\uD1A8\uB7EC\uC758 \uB2E4\uC911 \uC778\uC2A4\uD134\uC2A4\uAC00 \uC2E4\uD589\uC911\uC785\uB2C8\uB2E4. \
+ \uD558\uB098\uAC00 \uC644\uB8CC\uB420 \uB54C\uAE4C\uC9C0 \uC911\uB2E8\uB429\uB2C8\uB2E4.
+
+m.applet_stopped = Getdown \uC560\uD50C\uB9BF \uC2E4\uD589\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+
+# application/digest errors
+m.missing_appbase = \uC124\uC815 \uD30C\uC77C\uC5D0 'appbase' \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.
+m.invalid_version = \uC124\uC815 \uD30C\uC77C\uC5D0 \uC798\uBABB\uB41C \uBC84\uC804\uC774 \uBA85\uC2DC\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.
+m.invalid_appbase = \uC124\uC815 \uD30C\uC77C\uC5D0 \uC798\uBABB\uB41C 'appbase'\uAC00 \uBA85\uC2DC\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.
+m.missing_class = \uC124\uC815 \uD30C\uC77C\uC5D0 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158 \uD074\uB798\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.
+m.missing_code = \uC124\uC815 \uD30C\uC77C\uC5D0 \uB9AC\uC18C\uC2A4\uC5D0 \uB300\uD55C \uCF54\uB4DC\uAC00 \uBA85\uC2DC\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.
+m.invalid_digest_file = \uB2E4\uC774\uC81C\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
+
+# When at 0%
+m.initialising = Initialising
+m.starting = Starting
diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties
new file mode 100644 (file)
index 0000000..c9e0be1
--- /dev/null
@@ -0,0 +1,122 @@
+#
+# $Id$
+#
+# Getdown translation messages
+
+m.abort_title = Cancelar a instala\u00E7\u00E3o?
+m.abort_confirm = <html>Tem certeza que deseja cancelar a instala\u00E7\u00E3o? \
+  Voc\u00EA pode continuar a instala\u00E7\u00E3o mais tarde, \
+  basta executar a aplica\u00E7\u00E3o novamente.</html>
+m.abort_ok = Sair
+m.abort_cancel = Continuar a instala\u00E7\u00E3o
+
+m.detecting_proxy = Tentando detectar automaticamente as configura\u00E7\u00F5es de proxy
+
+m.configure_proxy = <html>N\u00E3o foi poss\u00EDvel conectar aos nossos servidores para \
+  fazer o download dos dados. \
+  <ul><li> Se o Firewall do Windows ou o Norton Internet Security est\u00E1 configurado \
+  para bloquear o programa <code>javaw.exe</code> n\u00E3o ser\u00E1 poss\u00EDvel realizar \
+  o download. Voc\u00EA ter\u00E1 que permitir que o programa <code>javaw.exe</code> acesse \
+  a internet. Voc\u00EA pode tentar executar o programa novamente, mas voc\u00EA precisa \
+  remover o programa javaw.exe das configura\u00E7\u00F5es do firewall (Iniciar -> Painel \
+  de controle -> Firewall do Windows).</ul> \
+  <p> Seu computador pode estar acessando a internet atrav\u00E9s de um proxy e n\u00E3o foi \
+  capaz de detectar automaticamente as configura\u00E7\u00F5es de proxy. \
+  Voc\u00EA pode informar esses dados abaixo.</html>
+
+m.proxy_extra = <html>Se voc\u00EA tem certeza que n\u00E3o usa um proxy, ent\u00E3o pode ser \
+  que exista um problema tempor\u00E1rio que est\u00E1 impedindo a comunica\u00E7\u00E3o \
+  com os nossos servidores. Neste caso voc\u00EA pode cancelar e tentar instalar novamente \
+  mais tarde.</html>
+
+m.proxy_host = IP do Proxy
+m.proxy_port = Porta do Proxy
+m.proxy_username = Nome de usu\u00e1rio
+m.proxy_password = Senha
+m.proxy_auth_required = Autentifica\u00e7\u00e3o requerida
+m.proxy_ok = OK
+m.proxy_cancel = Cancelar
+
+m.downloading_java = Fazendo o download da m\u00E1quina virtual Java
+m.unpacking_java = Descompactando a m\u00E1quina virtual Java
+
+m.resolving = Resolvendo downloads
+m.downloading = Transferindo dados
+m.failure = Download falhou: {0}
+
+m.checking = Verificando atualiza\u00E7\u00F5es
+m.validating = Validando
+m.patching = Atualizando
+m.launching = Executando
+
+m.patch_notes = Corrigir notas
+m.play_again = Jogar de novo
+
+m.complete = {0}% completo
+m.remain = {0} Permanecer
+
+m.updating_metadata = Transferindo arquivos de controle
+
+m.init_failed = Nosso arquivo de configura\u00E7\u00E3o est\u00E1 ausente ou corrompido. Tente \
+  baixar uma nova c\u00F3pia...
+
+m.java_download_failed = N\u00E3o conseguimos baixar automaticamente a\
+  vers\u00E3o necess\u00E1ria do Java para o seu computador.\n\n\
+  Por favor, acesse www.java.com, baixe e instale a \u00FAltima vers\u00E3o do \
+  Java, em seguida, tente executar o aplicativo novamente.
+
+m.java_unpack_failed = N\u00E3o conseguimos descompactar uma vers\u00E3o atualizada do \
+  Java. Por favor, certifique-se de ter pelo menos 100 MB de espa\u00E7o livre em seu \
+  disco r\u00EDgido e tente executar o aplicativo novamente. \n\n\
+  Se isso n\u00E3o resolver o problema, acesse www.java.com,baixe e \
+  instale a \u00FAltima vers\u00E3o do Java e tente novamente.
+
+m.unable_to_repair = N\u00E3o conseguimos baixar os arquivos necess\u00E1rios depois de \
+  cinco tentativas. Voc\u00EA pode tentar executar o aplicativo novamente, mas se ele \
+  falhar pode ser necess\u00E1rio desinstalar e reinstalar.
+
+m.unknown_error = A aplica\u00E7\u00E3o falhou ao iniciar devido a algum erro estranho \
+  do qual n\u00E3o conseguimos recuperar. Por favor, visite \n{0} para obter \
+  informa\u00E7\u00F5es sobre como recuperar.
+m.init_error = A aplica\u00E7\u00E3o falhou ao iniciar devido ao seguinte \
+  erro:\n{0}\n\nPor favor visite \n{1} para \
+  informa\u00E7\u00F5es sobre como lidar com esse problema.
+
+m.readonly_error =O diret\u00F3rio no qual este aplicativo est\u00E1 instalado: \
+  \n{0}\n \u00E9 somente leitura. Por favor, instale o aplicativo em um diret\u00F3rio onde \
+  voc\u00EA tem acesso de grava\u00E7\u00E3o.
+
+m.missing_resource = A aplica\u00E7\u00E3o falhou ao iniciar devido a uma falta \
+  de recurso:\n{0}\n\n Por favor, visite\n{1} para obter informa\u00E7\u00F5es sobre \
+  como lidar com tal problema.
+
+m.insufficient_permissions_error = Voc\u00EA n\u00E3o aceitou a assinatura digital \
+  do aplicativo. Se voc\u00EA quiser executar o aplicativo, voc\u00EA ter\u00E1 que aceitar \
+  a assinatura digital. \n\nPara fazer isso, voc\u00EA ter\u00E1 que sair do seu navegador, \
+  reinici\u00E1-lo, e retornar a esta p\u00E1gina web para executar a aplica\u00E7\u00E3o. \
+  Quando o di\u00E1logo de seguran\u00E7a aparecer, clique no bot\u00E3o para aceitar a \
+  assinatura digital e conceder a este aplicativo os privil\u00E9gios necess\u00E1rios \
+  para executar.
+
+m.corrupt_digest_signature_error = N\u00E3o conseguimos verificar a assinatura digital \
+  do aplicativo.\nPor favor, verifique se voc\u00EA est\u00E1 utilizando o aplicativo \nde um \
+  site correto.
+
+m.default_install_error = a se\u00E7\u00E3o de suporte do site
+
+m.another_getdown_running = V\u00E1rias inst\u00E2ncias desta aplica\u00E7\u00E3o \
+  est\u00E3o em execu\u00E7\u00E3o. Esta ir\u00E1 parar e deixar outra completar suas atividades.
+
+m.applet_stopped = Foi solicitado ao miniaplicativo GetDow que parasse de trabalhar.
+
+# application/digest errors
+m.missing_appbase = O arquivo de configura\u00E7\u00E3o n\u00E3o possui o 'AppBase'.
+m.invalid_version = O arquivo de configura\u00E7\u00E3o especifica uma vers\u00E3o inv\u00E1lida.
+m.invalid_appbase = O arquivo de configura\u00E7\u00E3o especifica um 'AppBase' inv\u00E1lido.
+m.missing_class = O arquivo de configura\u00E7\u00E3o n\u00E3o possui a classe de aplicativo.
+m.missing_code = O arquivo de configura\u00E7\u00E3o n\u00E3o especifica um recurso de c\u00F3digo.
+m.invalid_digest_file = O arquivo digest \u00E9 inv\u00E1lido.
+
+# When at 0%
+m.initialising = Initialising
+m.starting = Starting
diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties
new file mode 100644 (file)
index 0000000..33117ff
--- /dev/null
@@ -0,0 +1,65 @@
+#
+# $Id$
+#
+# Getdown translation messages
+
+m.detecting_proxy = \u641c\u5bfb\u4ee3\u7406\u670d\u52a1\u5668
+
+m.configure_proxy = <html>\u6211\u4eec\u65e0\u6cd5\u8fde\u63a5\u5230\u670d\u52a1\u5668\u4e0b\u8f7d\u6e38\u620f\u6570\u636e\u3002\u8fd9\u53ef\u80fd\u662f\u7531\u4e8e \
+  \u60a8\u7684\u8ba1\u7b97\u673a\u662f\u901a\u8fc7\u4ee3\u7406\u670d\u52a1\u5668\u8fde\u63a5\u4e92\u8054\u7f51\u7684\uff0c\u5e76\u4e14\u6211\u4eec\u65e0\u6cd5\u81ea\u52a8\u83b7\u5f97\u4ee3\u7406\u670d\u52a1\u5668\u7684 \
+  \u8bbe\u7f6e\u3002\u5982\u679c\u60a8\u77e5\u9053\u60a8\u4ee3\u7406\u670d\u52a1\u5668\u7684\u8bbe\u7f6e\uff0c\u60a8\u53ef\u4ee5\u5728\u4e0b\u9762\u8f93\u5165\u3002</html>
+
+m.proxy_extra = <html>\u5982\u679c\u60a8\u786e\u5b9a\u60a8\u6ca1\u6709\u4f7f\u7528\u4ee3\u7406\u670d\u52a1\u5668\uff0c\u8fd9\u53ef\u80fd\u662f\u7531\u4e8e\u6682\u65f6\u65e0\u6cd5 \
+  \u8fde\u63a5\u5230\u4e92\u8054\u7f51\uff0c\u5bfc\u81f4\u65e0\u6cd5\u548c\u670d\u52a1\u5668\u901a\u8baf\u3002\u8fd9\u79cd\u60c5\u51b5\uff0c\u60a8\u53ef\u4ee5\u53d6\u6d88\uff0c\u7a0d\u5019\u518d\u91cd\u65b0\u5b89\u88c5\u3002<br><br> \
+  \u5982\u679c\u60a8\u65e0\u6cd5\u786e\u5b9a\u60a8\u662f\u5426\u4f7f\u7528\u4e86\u4ee3\u7406\u670d\u52a1\u5668\uff0c\u8bf7\u8bbf\u95ee\u6211\u4eec\u7f51\u7ad9\u4e2d\u7684\u6280\u672f\u652f\u6301\u90e8\u4efd\uff0c \
+  \u4e86\u89e3\u5982\u4f55\u68c0\u6d4b\u60a8\u7684\u4ee3\u7406\u670d\u52a1\u5668\u8bbe\u7f6e\u3002</html>
+
+m.proxy_host = \u4ee3\u7406\u670d\u52a1\u5668\u7684IP\u5730\u5740
+m.proxy_port = \u4ee3\u7406\u670d\u52a1\u5668\u7684\u7aef\u53e3\u53f7
+m.proxy_username = Username
+m.proxy_password = Password
+m.proxy_auth_required = Authentication required
+m.proxy_ok = \u786e\u5b9a
+m.proxy_cancel = \u53d6\u6d88
+
+m.resolving = \u5206\u6790\u9700\u4e0b\u8f7d\u5185\u5bb9
+m.downloading = \u4e0b\u8f7d\u6570\u636e
+m.failure = \u4e0b\u8f7d\u5931\u8d25: {0}
+
+m.checking = \u68c0\u67e5\u66f4\u65b0\u5185\u5bb9
+m.validating = \u786e\u8ba4
+m.patching = \u5347\u7ea7
+m.launching = \u542f\u52a8
+
+m.complete = {0}% \u5b8c\u6210
+m.remain = {0} \u5269\u4f59\u65f6\u95f4
+
+m.updating_metadata = \u4e0b\u8f7d\u63a7\u5236\u6587\u4ef6
+
+m.init_failed = \u65e0\u6cd5\u627e\u5230\u914d\u7f6e\u6587\u4ef6\u6216\u5df2\u635f\u574f\u3002\u5c1d\u8bd5\u91cd\u65b0\u4e0b\u8f7d...
+
+m.unable_to_repair = \u7ecf\u8fc75\u6b21\u5c1d\u8bd5\uff0c\u4f9d\u7136\u65e0\u6cd5\u4e0b\u8f7d\u6240\u9700\u7684\u6587\u4ef6\u3002\
+\u60a8\u53ef\u4ee5\u91cd\u65b0\u8fd0\u884c\u7a0b\u5e8f\uff0c\u4f46\u662f\u5982\u679c\u4f9d\u7136\u5931\u8d25\uff0c\u60a8\u53ef\u80fd\u9700\u8981\u53cd\u5b89\u88c5\u5e76\u91cd\u65b0\u5b89\u88c5\u3002
+
+
+m.unknown_error = \u7531\u4e8e\u4e00\u4e9b\u65e0\u6cd5\u56de\u590d\u7684\u4e25\u91cd\u9519\u8bef\uff0c\u7a0b\u5e8f\u542f\u52a8\u5931\u8d25\u3002\
+\u8bf7\u8bbf\u95ee\u6211\u4eec\u7684\u7f51\u7ad9\u7684\u6280\u672f\u652f\u6301\u90e8\u4efd\uff0c\u4e86\u89e3\u5982\u4f55\u89e3\u51b3\u95ee\u9898\u3002
+
+m.init_error = \u7531\u4e8e\u4e0b\u5217\u9519\u8bef\uff0c\u7a0b\u5e8f\u542f\u52a8\u5931\u8d25\uff1a\n{0}\n\n \
+\u8bf7\u8bbf\u95ee\u6211\u4eec\u7684\u7f51\u7ad9\u7684\u6280\u672f\u652f\u6301\u90e8\u4efd\uff0c\u4e86\u89e3\u5982\u4f55\u5904\u7406\u8fd9\u4e9b\u9519\u8bef\u3002
+
+
+m.missing_resource = \u7531\u4e8e\u65e0\u6cd5\u627e\u5230\u4e0b\u5217\u8d44\u6e90\uff0c\u7a0b\u5e8f\u542f\u52a8\u5931\u8d25\uff1a\n{0}\n\n \
+\u8bf7\u8bbf\u95ee\u6211\u4eec\u7684\u7f51\u7ad9\u7684\u6280\u672f\u652f\u6301\u90e8\u4efd\uff0c\u4e86\u89e3\u5982\u4f55\u5904\u7406\u8fd9\u4e9b\u95ee\u9898\u3002
+
+# application/digest errors
+m.missing_appbase = \u914d\u7f6e\u6587\u4ef6\u4e2d\u65e0\u6cd5\u627e\u5230 'appbase'\u3002
+m.invalid_version = \u914d\u7f6e\u6587\u4ef6\u6307\u5b9a\u4e86\u65e0\u6548\u7684\u7248\u672c\u3002
+m.invalid_appbase = \u914d\u7f6e\u6587\u4ef6\u6307\u5b9a\u4e86\u65e0\u6548\u7684 'appbase'\u3002
+m.missing_class = \u914d\u7f6e\u6587\u4ef6\u4e2d\u65e0\u6cd5\u627e\u5230\u7a0b\u5e8f\u6587\u4ef6\u3002
+m.missing_code = \u914d\u7f6e\u6587\u4ef6\u4e2d\u65e0\u6cd5\u627e\u5230\u6307\u5b9a\u7684\u8d44\u6e90\u3002
+m.invalid_digest_file = \u65e0\u6548\u7684\u914d\u7f6e\u6587\u4ef6\u3002
+
+# When at 0%
+m.initialising = Initialising
+m.starting = Starting
diff --git a/getdown/src/getdown/lib/SOURCE_HEADER b/getdown/src/getdown/lib/SOURCE_HEADER
new file mode 100644 (file)
index 0000000..43271fe
--- /dev/null
@@ -0,0 +1,5 @@
+//
+// 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/commons-compress-1.18.jar b/getdown/src/getdown/lib/commons-compress-1.18.jar
new file mode 100644 (file)
index 0000000..e401046
Binary files /dev/null and b/getdown/src/getdown/lib/commons-compress-1.18.jar differ
diff --git a/getdown/src/getdown/lib/jRegistryKey.dll b/getdown/src/getdown/lib/jRegistryKey.dll
new file mode 100644 (file)
index 0000000..5746728
Binary files /dev/null and b/getdown/src/getdown/lib/jRegistryKey.dll 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
new file mode 100644 (file)
index 0000000..5100795
Binary files /dev/null and b/getdown/src/getdown/lib/jregistrykey/jregistrykey/1.0/jregistrykey-1.0.jar 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
new file mode 100644 (file)
index 0000000..226a7d7
--- /dev/null
@@ -0,0 +1,9 @@
+<?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
new file mode 100644 (file)
index 0000000..1a8a725
--- /dev/null
@@ -0,0 +1,12 @@
+<?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
new file mode 100644 (file)
index 0000000..3be50cc
--- /dev/null
@@ -0,0 +1,6 @@
+Main-Class: com.threerings.getdown.launcher.Getdown
+Permissions: all-permissions
+Application-Name: Getdown
+Codebase: *
+Application-Library-Allowable-Codebase: *
+Caller-Allowable-Codebase: *
diff --git a/getdown/src/getdown/mvn_cmd b/getdown/src/getdown/mvn_cmd
new file mode 100755 (executable)
index 0000000..0cd54eb
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+if [ x$JVLVERSION != x ]; then
+  export VERSION=$JVLVERSION
+else
+  export VERSION=1.8.3-1.1.8_JVL
+fi
+
+if [ x${VERSION%_JVL} = x$VERSION ]; then
+  VERSION=${VERSION}_JVL
+fi
+
+echo "Setting VERSION to '$VERSION'"
+perl -p -i -e 's|(<version>)[^<]*JVL[^<]*(</version>)|${1}$ENV{VERSION}${2}|;' pom.xml */pom.xml
+mvn package -Dgetdown.host.whitelist="jalview.org,*.jalview.org" -Dallow_file_protocol=false -Dconnect_timeout=8 -Dread_timeout=15
+RET=$?
+if [ x$RET = x0 ]; then
+  cp launcher/target/getdown-launcher-$VERSION.jar ../../../getdown/lib/getdown-launcher.jar && echo "Copied getdown-launcher-$VERSION.jar to getdown/lib"
+  cp core/target/getdown-core-$VERSION.jar ../../../getdown/lib/getdown-core.jar && echo "Copied getdown-core-$VERSION.jar to getdown/lib"
+  cp core/target/getdown-core-$VERSION.jar ../../../j8lib/getdown-core.jar && echo "Copied getdown-core-$VERSION.jar to j8lib"
+  cp core/target/getdown-core-$VERSION.jar ../../../j11lib/getdown-core.jar && echo "Copied getdown-core-$VERSION.jar to j11lib"
+fi
+
+VERSION=${VERSION/JVL/FJVL}
+echo "Setting VERSION to '$VERSION'"
+perl -p -i -e 's|(<version>)[^<]*JVL[^<]*(</version>)|${1}$ENV{VERSION}${2}|;' pom.xml */pom.xml
+mvn package -Dgetdown.host.whitelist="jalview.org,*.jalview.org" -Dallow_file_protocol=true -Dconnect_timeout=4 -Dread_timeout=4
+RET=$?
+if [ x$RET = x0 ]; then
+  cp launcher/target/getdown-launcher-$VERSION.jar ../../../getdown/lib/getdown-launcher-local.jar && echo "Copied getdown-launcher-$VERSION.jar to getdown/lib/getdown-launcher-local.jar"
+fi
diff --git a/getdown/src/getdown/pom.xml b/getdown/src/getdown/pom.xml
new file mode 100644 (file)
index 0000000..a73b59e
--- /dev/null
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.sonatype.oss</groupId>
+    <artifactId>oss-parent</artifactId>
+    <version>7</version>
+  </parent>
+
+  <groupId>com.threerings.getdown</groupId>
+  <artifactId>getdown</artifactId>
+  <packaging>pom</packaging>
+  <version>1.8.3-1.1.8_FJVL</version>
+
+  <name>getdown</name>
+  <description>An application installer and updater.</description>
+  <url>https://github.com/threerings/getdown</url>
+  <issueManagement>
+    <url>https://github.com/threerings/getdown/issues</url>
+  </issueManagement>
+
+  <licenses>
+    <license>
+      <name>The (New) BSD License</name>
+      <url>http://www.opensource.org/licenses/bsd-license.php</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+
+  <developers>
+    <developer>
+      <id>samskivert</id>
+      <name>Michael Bayne</name>
+      <email>mdb@samskivert.com</email>
+    </developer>
+  </developers>
+
+  <scm>
+    <connection>scm:git:git://github.com/threerings/getdown.git</connection>
+    <developerConnection>scm:git:git@github.com:threerings/getdown.git</developerConnection>
+    <url>https://github.com/threerings/getdown</url>
+  </scm>
+
+  <modules>
+    <module>core</module>
+    <module>launcher</module>
+    <module>ant</module>
+  </modules>
+
+  <repositories>
+       <repository>
+          <id>ej-technologies</id>
+          <url>https://maven.ej-technologies.com/repository</url>
+       </repository>
+  </repositories>
+
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-compress</artifactId>
+      <version>1.18</version>
+    </dependency>
+    <dependency>
+               <groupId>com.install4j</groupId>
+               <artifactId>install4j-runtime</artifactId>
+               <version>7.0.11</version>
+               <scope>provided</scope>
+       </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.sonatype.plugins</groupId>
+        <artifactId>nexus-staging-maven-plugin</artifactId>
+        <version>1.6.8</version>
+        <extensions>true</extensions>
+        <inherited>false</inherited>
+        <configuration>
+          <serverId>ossrh-releases</serverId>
+          <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+          <stagingProfileId>aa555c46fc37d0</stagingProfileId>
+        </configuration>
+      </plugin>
+    </plugins>
+
+    <!-- Common plugin configuration for all children -->
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-compiler-plugin</artifactId>
+          <version>3.8.1</version>
+          <configuration>
+            <source>1.8</source>
+            <target>1.8</target>
+            <fork>true</fork>
+            <showDeprecation>true</showDeprecation>
+            <showWarnings>true</showWarnings>
+            <compilerArgs>
+              <arg>-Xlint</arg>
+              <arg>-Xlint:-serial</arg>
+              <arg>-Xlint:-path</arg>
+            </compilerArgs>
+          </configuration>
+        </plugin>
+
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-resources-plugin</artifactId>
+          <version>3.0.2</version>
+          <configuration>
+            <encoding>UTF-8</encoding>
+          </configuration>
+        </plugin>
+
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <version>3.0.0-M1</version>
+          <configuration>
+            <quiet>true</quiet>
+            <show>public</show>
+            <additionalparam>-Xdoclint:all -Xdoclint:-missing</additionalparam>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>eclipse</id>
+      <activation>
+        <property>
+          <name>m2e.version</name>
+        </property>
+      </activation>
+      <build>
+        <pluginManagement>
+          <plugins>
+            <plugin>
+              <!-- Tell m2eclipse to ignore the enforcer plugin from our parent. Otherwise it warns
+                   about not being able to run it. -->
+              <groupId>org.eclipse.m2e</groupId>
+              <artifactId>lifecycle-mapping</artifactId>
+              <version>1.0.0</version>
+              <configuration>
+                <lifecycleMappingMetadata>
+                  <pluginExecutions>
+                    <pluginExecution>
+                      <pluginExecutionFilter>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-enforcer-plugin</artifactId>
+                        <versionRange>[1.0,)</versionRange>
+                        <goals>
+                          <goal>enforce</goal>
+                        </goals>
+                      </pluginExecutionFilter>
+                      <action>
+                        <ignore />
+                      </action>
+                    </pluginExecution>
+                  </pluginExecutions>
+                </lifecycleMappingMetadata>
+              </configuration>
+            </plugin>
+          </plugins>
+        </pluginManagement>
+      </build>
+    </profile>
+
+    <profile>
+      <id>release-sign-artifacts</id>
+      <activation>
+        <property><name>performRelease</name><value>true</value></property>
+      </activation>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-gpg-plugin</artifactId>
+            <version>1.6</version>
+            <executions>
+              <execution>
+                <id>sign-artifacts</id>
+                <phase>verify</phase>
+                <goals>
+                  <goal>sign</goal>
+                </goals>
+              </execution>
+            </executions>
+            <configuration>
+              <keyname>mdb@samskivert.com</keyname>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+</project>
diff --git a/gradle.properties b/gradle.properties
new file mode 100644 (file)
index 0000000..2dbdad3
--- /dev/null
@@ -0,0 +1,142 @@
+jalviewDir = .
+
+#JAVA_VERSION = 1.8
+JAVA_VERSION = 11
+JALVIEW_VERSION = DEVELOPMENT
+INSTALLATION = Source
+jalview_keystore = keys/.keystore
+jalview_keystore.pass = alignmentisfun
+jalview_key = jalview
+jalview_key.pass = alignmentisfun
+jalview_tsaurl =
+proxyPort = 80
+proxyHost = sqid
+jalview_keyalg = SHA1withRSA
+jalview_keydig = SHA1
+
+testngGroups = Functional
+
+j8libDir = j8lib
+j11libDir = j11lib
+resourceDir = resources
+helpParentDir = help
+helpDir = help
+helpOutputDir = build/distributions/help
+docDir = doc
+sourceDir = src
+schemaDir = schemas
+classesDir = classes
+examplesDir = examples
+clover = false
+use_clover = false
+cloverClassesDir = clover-classes
+cloverSourcesInstrDir = sources-instr
+packageDir = dist
+ARCHIVEDIR =
+outputJar = jalview.jar
+
+testSourceDir = test
+testOutputDir = tests
+utilsDir = utils
+
+buildPropertiesFile = .build_properties
+application_codebase = *.jalview.org
+mainClass = jalview.bin.Jalview
+shadowJarMainClass = jalview.bin.Launcher
+launcherClass = jalview.bin.Jalview
+
+gradlePluginsDir = gradle/plugins
+
+getdown_local = false
+getdown_website_dir = getdown/website
+getdown_resource_dir = resource
+#getdown_j11lib_dir = j11lib
+getdown_files_dir = getdown/files
+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_launch_jvl = channel_launch.jvl
+getdown_build_properties = build_properties
+getdown_txt_title = Jalview
+getdown_txt_allow_offline = true
+getdown_txt_max_concurrent_downloads = 10
+getdown_txt_jalview.jvmmempc = 90
+getdown_txt_multi_jvmarg = -Dgetdownappdir=%APPDIR%
+getdown_txt_strict_comments = true
+getdown_txt_title = Jalview
+getdown_txt_ui.name = Jalview
+getdown_txt_ui.progress_sync_before_shown = true
+getdown_txt_ui.progress_sync_after_shown = false
+getdown_txt_ui.keep_on_top = true
+getdown_txt_ui.display_appbase = true
+getdown_txt_ui.display_version = true
+getdown_txt_ui.min_show_seconds = 6
+getdown_txt_ui.background = FFFFFF
+getdown_txt_ui.background_image = utils/getdown/jalview_logo_background_getdown-640x480.png
+getdown_txt_ui.instant_background_image = utils/getdown/jalview_logo_background_getdown_instant-640x480.png
+getdown_txt_ui.error_background = utils/getdown/jalview_logo_background_getdown_error-640x480.png
+getdown_txt_ui.progress_image = utils/getdown/jalview_logo_background_getdown-progress.png
+getdown_txt_ui.icon = resources/images/JalviewLogo_Huge.png
+getdown_txt_ui.progress = 20, 440, 600, 22
+getdown_txt_ui.progress_bar = AAAAFF
+getdown_txt_ui.progress_text = 000000
+getdown_txt_ui.status = 20, 380, 600, 58
+getdown_txt_ui.status_text = 000066
+#getdown_txt_ui.text_shadow = FFFFFF
+getdown_txt_ui.install_error = http://www.jalview.org/faq/getdownerror
+getdown_txt_ui.mac_dock_icon = resources/images/jalview_logos.ico
+getdown_alt_java8_min_version  = 01080000
+getdown_alt_java8_max_version  = 01089999
+getdown_alt_java11_min_version = 11000000
+getdown_alt_java11_max_version =
+getdown_alt_java11_txt_multi_java_location = [windows-amd64] /getdown/jre/windows-jre11.jar,[linux-amd64] /getdown/jre/linux-jre11.tgz,[mac os x] /getdown/jre/macos-jre11.tgz
+getdown_alt_java8_txt_multi_java_location = [windows-amd64] /getdown/jre/windows-jre1.8.tgz,[linux-amd64] /getdown/jre/linux-jre1.8.tgz,[mac os x] /getdown/jre/macos-jre1.8.tgz
+JRE_installs = /Users/bsoares/Java/installs
+Windows_JRE8 = OpenJDK8U-jdk_x64_windows_hotspot_8u202b08/jdk8u202-b08
+Mac_JRE8 = OpenJDK8U-jre_x64_mac_hotspot_8u202b08/jdk8u202-b08-jre/Contents/Home
+Linux_JRE8 = OpenJDK8U-jre_x64_linux_hotspot_8u202b08/jdk8u202-b08-jre
+Windows_JRE11 = OpenJDK11-jre_x64_windows_hotspot_11_28/jdk-11+28-jre
+Mac_JRE11 = OpenJDK11-jre_x64_mac_hotspot_11_28/jdk-11+28-jre/Contents/Home
+Linux_JRE11 = OpenJDK11-jre_x64_linux_hotspot_11_28/jdk-11+28-jre
+
+j8libDir = j8lib
+j11libDir = j11lib
+j11modDir = j11mod
+j11modules = com.sun.istack.runtime,com.sun.xml.bind,com.sun.xml.fastinfoset,com.sun.xml.streambuffer,com.sun.xml.txw2,com.sun.xml.ws.policy,java.activation,java.annotation,java.base,java.compiler,java.datatransfer,java.desktop,java.logging,java.management,java.management.rmi,java.naming,java.prefs,java.rmi,java.scripting,java.security.sasl,java.sql,java.xml,java.xml.bind,java.xml.soap,java.xml.ws,javax.jws,jdk.httpserver,jdk.jsobject,jdk.unsupported,jdk.xml.dom,org.jvnet.mimepull,org.jvnet.staxex,javax.servlet.api,java.ws.rs
+
+install4jCopyrightMessage = ...
+install4jMacOSBundleId = org.jalview.jalview-desktop
+install4jResourceDir = utils/install4j
+install4jTemplate = install4j_template.install4j
+install4jInfoPlistFileAssociations = file_associations_auto-Info_plist.xml
+install4jInstallerFileAssociations = file_associations_auto-install4j.xml
+install4jDMGUninstallerAppFiles = uninstall_old_jalview_files.xml
+install4jBuildDir = build/install4j
+install4jMediaTypes = windows,macosArchive,linuxRPM,linuxDeb,unixArchive,unixInstaller
+install4jFaster = false
+
+OSX_KEYSTORE =
+OSX_KEYPASS =
+JSIGN_SH = echo
+
+eclipse_extra_jdt_prefs_file = .settings/org.eclipse.jdt.core.jalview.prefs
+
+pandoc_exec = /usr/local/bin/pandoc,/usr/bin/pandoc
+dev = false
+
+CHANNEL=LOCAL
+getdown_channel_base = http://www.jalview.org/getdown
+getdown_channel_name = SCRATCH-DEFAULT
+getdown_app_dir_release = release
+getdown_app_dir_alt = alt
+getdown_install_dir = install
+getdown_rsync_dest = /Volumes/jalview/docroot/getdown
+reportRsyncCommand =
+RUNRSYNC=false
+
+bamboo_channelbase = https://builds.jalview.org/browse
+bamboo_planKey = 
+bamboo_getdown_channel_suffix = /latest/artifact/shared/getdown-channel
\ No newline at end of file
similarity index 100%
rename from help/help.hs
rename to help/help/help.hs
similarity index 98%
rename from help/help.jhm
rename to help/help/help.jhm
index 1666cc6..47f7d53 100755 (executable)
@@ -61,6 +61,7 @@
    <mapID target="strucprefs" url="html/features/preferences.html#structure"/>     
    <mapID target="commandline" url="html/features/commandline.html"/>
    <mapID target="clarguments" url="html/features/clarguments.html"/>
+   <mapID target="jvlfiles" url="html/features/jvlfiles.html"/>
    <mapID target="io" url="html/io/index.html"/>
    <mapID target="io.modellerpir" url="html/io/modellerpir.html"/>
    <mapID target="io.seqreport" url="html/io/exportseqreport.html"/>
    <mapID target="groovy.colours" url="html/features/groovy.html#groovyColours" />
    <mapID target="groovy.featurescounter" url="html/groovy/featuresCounter.html" />
    <mapID target="privacy" url="html/privacy.html" />
-   <mapID target="vamsas" url="html/vamsas/index.html"/>
    <mapID target="aminoAcids" url="html/misc/aminoAcids.html" />
    <mapID target="aaProperties" url="html/misc/aaproperties.html" />
    <mapID target="geneticCode" url="html/misc/geneticCode.html" />
    
    <mapID target="overview" url="html/features/overview.html" />
    <mapID target="overviewprefs" url="html/features/preferences.html#overview" />
+   
+   <mapID target="importvcf" url="html/features/importvcf.html" />
 </map>
similarity index 95%
rename from help/helpTOC.xml
rename to help/help/helpTOC.xml
index 77ddd88..7e358d6 100755 (executable)
@@ -24,8 +24,9 @@
        <tocitem text="Jalview Documentation" target="home" expand="true">
                        <tocitem text="What's new" target="new" expand="true">
                                <tocitem text="Latest Release Notes" target="release"/>
-                               <tocitem text="Structure Chooser" target="pdbchooser"/>
-                               <tocitem text="Chimera Annotation Exchange" target="chimera.annotxfer"/>
+                               <tocitem text="VCF Import" target="importvcf"/>
+                               <tocitem text="Feature Filters and Attribute Colourschemes" target="features.featureschemes" />
+                               
                </tocitem>
                
                <tocitem text="Editing Alignments" target="edit" />
@@ -49,6 +50,7 @@
                <tocitem text="Select Columns by Annotation" target="selectcolbyannot" />
                
                <tocitem text="Nucleic Acid Support" target="nucleicAcids" expand="false">
+                 <tocitem text="Annotating from VCF" target="importvcf" /> 
                        <tocitem text="Viewing RNA structure" target="varna" />
                        <tocitem text="RNA Structure Consensus" target="calcs.alstrconsensus" />
                        <tocitem text="RNA Helices coloring" target="colours.rnahelices" />
                <tocitem text="Opening URLs from Jalview" target="urllinks" expand="true">
                    <tocitem text="Configuring URL Links" target="linksprefs" />
                </tocitem>
-               <tocitem text="VAMSAS Data Exchange" target="vamsas">
-                       <!-- what can Jalview share with other apps -->
-                       <!-- what other apps exist -->
-               </tocitem>
                <tocitem text="Window Menus" target="menus" expand="false">
                        <tocitem text="Desktop Window" target="desktopMenu" />
                        <tocitem text="Alignment Window" target="alMenu">
                        </tocitem>
                </tocitem>
                <tocitem text="Preferences" target="preferences" />
-               <tocitem text="Memory Settings" target="memory" expand="false"/>
                <tocitem text="Scripting with Groovy" target="groovy">
                        <tocitem text="Groovy Features Counter example" target="groovy.featurescounter"/>
                </tocitem>
                <tocitem text="Command Line" target="commandline" expand="false">
                        <tocitem text="Command Line Arguments" target="clarguments" />
-               </tocitem>
+    <tocitem text="Memory Settings" target="memory" expand="false"/>
+    <tocitem text="Jalview Launch Files" target="jvlfiles" expand="false"/>
+    </tocitem>
                <tocitem text="Privacy" target="privacy" />
        </tocitem>
        <tocitem text="Useful information" expand="true">
similarity index 100%
rename from help/html/align.jpg
rename to help/help/html/align.jpg
similarity index 94%
rename from help/html/calculations/pca.html
rename to help/help/html/calculations/pca.html
index 3529cae..3bf21cb 100755 (executable)
@@ -70,7 +70,7 @@
       nucleotide substitution matrix</a>, or by sequence percentage identity,
       or sequence feature similarity. 
   </p>
-  <img src="pcaviewer.gif">
+  <img src="pcaviewer.png">
   <p>
     <strong>The PCA Viewer</strong>
   </p>
@@ -84,7 +84,9 @@
     towards the front of the view.</p>
   <p>
     The 3d view can be rotated by dragging the mouse with the <strong>left
-      mouse button</strong> pressed. The view can also be zoomed in and out with
+      mouse button</strong> pressed, or with the <strong>arrow
+      keys</strong> when <strong>SHIFT</strong> is pressed. The
+    view can also be zoomed in and out with
     the up and down <strong>arrow keys</strong> (and the roll bar of the
     mouse if present). Labels will be shown for each sequence if the
     entry in the View menu is checked, and the plot background colour
@@ -122,7 +124,7 @@ left-clicking and dragging the mouse over the display. -->
   <p>
     Initially, the display shows the first three components of the
     similarity space, but any eigenvector can be used by changing the
-    selected dimension for the x, y, or z axis through each ones menu
+    selected dimension for the x, y, or z axis through each one's menu
     located below the 3d display. The <strong><em>Reset</em></strong>
     button will reset axis and rotation settings to their defaults.
   </p>
@@ -131,7 +133,7 @@ left-clicking and dragging the mouse over the display. -->
     <em>The output of points and transformed point coordinates was
       added to the Jalview desktop in v2.7.</em> <em>The Reset button
       and Change Parameters menu were added in Jalview 2.8.</em> <em>Support
-      for PAM250 based PCA was added in Jalview 2.8.1.</em>
+      for PAM250 based PCA was added in Jalview 2.8.1.</em><em>In Jalview 2.11, support for saving and restoring PCAs in Project files was added, and the Change parameters menu removed.</em> 
   </p>
   <p>
     <strong>Reproducing PCA calculations performed with older
diff --git a/help/help/html/calculations/pcaviewer.png b/help/help/html/calculations/pcaviewer.png
new file mode 100644 (file)
index 0000000..7dc5b80
Binary files /dev/null and b/help/help/html/calculations/pcaviewer.png differ
similarity index 88%
rename from help/html/calculations/treeviewer.html
rename to help/help/html/calculations/treeviewer.html
index 8cb2b59..f326f19 100755 (executable)
     alignment, such as another tree viewer.
   </p>
   <p>
-    <strong><em><a name="partitioning">Grouping sequences by partitioning</a> the
-        tree at a particular distance</em></strong><br> Clicking anywhere along
-    the extent of the tree (but not on a leaf or internal node) defines
-    a tree 'partition', by cutting every branch of the tree spanning the
-    depth where the mouse-click occurred. Groups are created containing
-    sequences at the leaves of each connected sub tree. These groups are
-    each given a different colour, which are reflected in other windows
-    in the same way as if the sequence IDs were selected, and can be
-    edited in the same way as user defined sequence groups.
-  </p>
+               <strong><em><a name="partitioning">Grouping
+                                       sequences by partitioning</a> the tree at a particular distance</em></strong><br>
+               Clicking anywhere along the extent of the tree (but not on a leaf or
+               internal node) defines a tree 'partition', by cutting every branch of
+               the tree spanning the depth where the mouse-click occurred. A red line
+               will be shown where the partition was made, and groups are created
+               containing sequences at the leaves of each connected sub tree. These
+               groups are each given a different colour, which are reflected in other
+               windows in the same way as if the sequence IDs were selected, and can
+               be edited in the same way as user defined sequence groups.
+       </p>
   <p>
     Tree partitions are useful for comparing clusters produced by
     different methods and measures. They are also an effective way of
diff --git a/help/help/html/colourSchemes/index.html b/help/help/html/colourSchemes/index.html
new file mode 100755 (executable)
index 0000000..f0ae161
--- /dev/null
@@ -0,0 +1,432 @@
+<html>
+<!--
+ * 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.
+ -->
+<head>
+<title>Colour Schemes</title>
+<style type="text/css">
+<!--
+td {
+       font-family: "Courier New", Courier, mono;
+       font-style: normal;
+       font-size: medium;
+}
+-->
+</style>
+</head>
+<body>
+  <p>
+    <strong>Colour schemes</strong>
+  </p>
+  <p>Jalview allows the user to set a colour scheme for the
+    whole alignment view or for each group defined on regions within it.</p>
+  <p>To change the colour for a view, simply select a new colour scheme from
+    the &quot;Colour&quot; menu.</p>
+  <p>To change the colour of a group, right click on any residue
+    within a group and use the popup menu to define the group colour.</p>
+  <p>At the top of the &quot;Colour&quot; menu you'll see a tick box
+    &quot;Apply Colour to all groups&quot;. This is ticked by
+    default so that a chosen colour scheme will be applied to all
+    existing groups. If you wish to maintain the colour scheme for
+    defined groups, make sure you deselect this option before selecting
+    a new scheme in the Colour menu.</p>
+  <p>
+    The <strong>&quot;Colour&#8594;<a
+      href="textcolour.html">Colour Text...</a>&quot;
+    </strong> entry opens a dialog box to set a different text colour for light
+    and dark background, and the intensity threshold for transition
+    between them.
+  </p>
+       <p>
+               <strong>Dynamic colour schemes</strong>
+       </p>
+       <p>
+               Alignments and groups may be coloured according to <a
+                       href="annotationColouring.html">associated sequence and alignment
+                       annotation</a>. Any applied colour schemes (such as those shown in the
+               table below) can also be <a href="conservation.html">shaded by
+                       amino acid physico-chemical property conservation</a> to reveal patterns
+               of variation, or applied selectively to <a href="abovePID.html">Above
+                       Identity Threshold</a>.
+       </p>
+       <p>
+               Alignments and groups can also be shaded according to percentage
+               abundance of aligned residues (using <a href="pid.html">Percentage
+                       Identity</a>), and for protein alignments, the <a href="blosum.html">BLOSUM
+                       62 Score</a> and <a href="clustal.html">ClustalX</a> shading schemes
+               highlight amino acid conservation.
+       </p>
+       <p>
+               The <strong>Sequence ID</strong> colour scheme shades sequences with
+               the same colour assigned to their Sequence ID. Colours are assigned
+               when groups are created by <a href="../calculations/treeviewer.html">partitioning
+                       a tree</a> or subdividing the alignment by sequence with <strong>Select&#8594;Make
+                       Groups For Selection</strong>.
+       </p>
+       <p>
+               <strong>Amino acid and Nucleotide based colour schemes</strong>
+       <p>The built-in colour schemes based on amino acid and nucleotide symbols are
+               summarised in the table below:
+       
+       <div align="center"><p>&nbsp;</p>
+               <p>
+                       <strong>Protein Colour Schemes</strong>
+               </p>
+               <table border="1">
+                       <tr>
+                               <td>
+                                       <table border="1">
+                                               <tr>
+                                                       <td nowrap></td>
+                                                       <td>A</td>
+                                                       <td>R</td>
+                                                       <td>N</td>
+                                                       <td>D</td>
+                                                       <td>C</td>
+                                                       <td>Q</td>
+                                                       <td>E</td>
+                                                       <td>G</td>
+                                                       <td>H</td>
+                                                       <td>I</td>
+                                                       <td>L</td>
+                                                       <td>K</td>
+                                                       <td>M</td>
+                                                       <td>F</td>
+                                                       <td>P</td>
+                                                       <td>S</td>
+                                                       <td>T</td>
+                                                       <td>W</td>
+                                                       <td>Y</td>
+                                                       <td>V</td>
+                                                       <td>B</td>
+                                                       <td>X</td>
+                                                       <td>Z</td>
+                                               </tr>
+                                               <tr>
+                                                       <td height="24">Clustal</td>
+                                                       <td bgcolor="#80a0f0"></td>
+                                                       <td bgcolor="#f01505"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#c048c0"></td>
+                                                       <td bgcolor="#f08080"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#c048c0"></td>
+                                                       <td bgcolor="#f09048"></td>
+                                                       <td bgcolor="#15a4a4"></td>
+                                                       <td bgcolor="#80a0f0"></td>
+                                                       <td bgcolor="#80a0f0"></td>
+                                                       <td bgcolor="#f01505"></td>
+                                                       <td bgcolor="#80a0f0"></td>
+                                                       <td bgcolor="#80a0f0"></td>
+                                                       <td bgcolor="#ffff00"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#80a0f0"></td>
+                                                       <td bgcolor="#15a4a4"></td>
+                                                       <td bgcolor="#80a0f0"></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                               </tr>
+                                               <tr>
+                                                       <td height="24">Zappo</td>
+                                                       <td bgcolor="#ffafaf"></td>
+                                                       <td bgcolor="#6464ff"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#ff0000"></td>
+                                                       <td bgcolor="#ffff00"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#ff0000"></td>
+                                                       <td bgcolor="#ff00ff"></td>
+                                                       <td bgcolor="#6464ff"></td>
+                                                       <td bgcolor="#ffafaf"></td>
+                                                       <td bgcolor="#ffafaf"></td>
+                                                       <td bgcolor="#6464ff"></td>
+                                                       <td bgcolor="#ffafaf"></td>
+                                                       <td bgcolor="#ffc800"></td>
+                                                       <td bgcolor="#ff00ff"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#ffc800"></td>
+                                                       <td bgcolor="#ffc800"></td>
+                                                       <td bgcolor="#ffafaf"></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                               </tr>
+                                               <tr>
+                                                       <td>Taylor</td>
+                                                       <td bgcolor="#ccff00"></td>
+                                                       <td bgcolor="#0000ff"></td>
+                                                       <td bgcolor="#cc00ff"></td>
+                                                       <td bgcolor="#ff0000"></td>
+                                                       <td bgcolor="#ffff00"></td>
+                                                       <td bgcolor="#ff00cc"></td>
+                                                       <td bgcolor="#ff0066"></td>
+                                                       <td bgcolor="#ff9900"></td>
+                                                       <td bgcolor="#0066ff"></td>
+                                                       <td bgcolor="#66ff00"></td>
+                                                       <td bgcolor="#33ff00"></td>
+                                                       <td bgcolor="#6600ff"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#00ff66"></td>
+                                                       <td bgcolor="#ffcc00"></td>
+                                                       <td bgcolor="#ff3300"></td>
+                                                       <td bgcolor="#ff6600"></td>
+                                                       <td bgcolor="#00ccff"></td>
+                                                       <td bgcolor="#00ffcc"></td>
+                                                       <td bgcolor="#99ff00"></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                               </tr>
+                                               <tr>
+                                                       <td>Hydrophobicity</td>
+                                                       <td bgcolor="#ad0052"></td>
+                                                       <td bgcolor="#0000ff"></td>
+                                                       <td bgcolor="#0c00f3"></td>
+                                                       <td bgcolor="#0c00f3"></td>
+                                                       <td bgcolor="#c2003d"></td>
+                                                       <td bgcolor="#0c00f3"></td>
+                                                       <td bgcolor="#0c00f3"></td>
+                                                       <td bgcolor="#6a0095"></td>
+                                                       <td bgcolor="#1500ea"></td>
+                                                       <td bgcolor="#ff0000"></td>
+                                                       <td bgcolor="#ea0015"></td>
+                                                       <td bgcolor="#0000ff"></td>
+                                                       <td bgcolor="#b0004f"></td>
+                                                       <td bgcolor="#cb0034"></td>
+                                                       <td bgcolor="#4600b9"></td>
+                                                       <td bgcolor="#5e00a1"></td>
+                                                       <td bgcolor="#61009e"></td>
+                                                       <td bgcolor="#5b00a4"></td>
+                                                       <td bgcolor="#4f00b0"></td>
+                                                       <td bgcolor="#f60009"></td>
+                                                       <td bgcolor="#0c00f3"></td>
+                                                       <td bgcolor="#680097"></td>
+                                                       <td bgcolor="#0c00f3"></td>
+                                               </tr>
+                                               <tr>
+                                                       <td>Helix Propensity</td>
+                                                       <td bgcolor="#e718e7"></td>
+                                                       <td bgcolor="#6f906f"></td>
+                                                       <td bgcolor="#1be41b"></td>
+                                                       <td bgcolor="#778877"></td>
+                                                       <td bgcolor="#23dc23"></td>
+                                                       <td bgcolor="#926d92"></td>
+                                                       <td bgcolor="#ff00ff"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#758a75"></td>
+                                                       <td bgcolor="#8a758a"></td>
+                                                       <td bgcolor="#ae51ae"></td>
+                                                       <td bgcolor="#a05fa0"></td>
+                                                       <td bgcolor="#ef10ef"></td>
+                                                       <td bgcolor="#986798"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#36c936"></td>
+                                                       <td bgcolor="#47b847"></td>
+                                                       <td bgcolor="#8a758a"></td>
+                                                       <td bgcolor="#21de21"></td>
+                                                       <td bgcolor="#857a85"></td>
+                                                       <td bgcolor="#49b649"></td>
+                                                       <td bgcolor="#758a75"></td>
+                                                       <td bgcolor="#c936c9"></td>
+                                               </tr>
+                                               <tr>
+                                                       <td nowrap>Strand Propensity</td>
+                                                       <td bgcolor="#5858a7"></td>
+                                                       <td bgcolor="#6b6b94"></td>
+                                                       <td bgcolor="#64649b"></td>
+                                                       <td bgcolor="#2121de"></td>
+                                                       <td bgcolor="#9d9d62"></td>
+                                                       <td bgcolor="#8c8c73"></td>
+                                                       <td bgcolor="#0000ff"></td>
+                                                       <td bgcolor="#4949b6"></td>
+                                                       <td bgcolor="#60609f"></td>
+                                                       <td bgcolor="#ecec13"></td>
+                                                       <td bgcolor="#b2b24d"></td>
+                                                       <td bgcolor="#4747b8"></td>
+                                                       <td bgcolor="#82827d"></td>
+                                                       <td bgcolor="#c2c23d"></td>
+                                                       <td bgcolor="#2323dc"></td>
+                                                       <td bgcolor="#4949b6"></td>
+                                                       <td bgcolor="#9d9d62"></td>
+                                                       <td bgcolor="#c0c03f"></td>
+                                                       <td bgcolor="#d3d32c"></td>
+                                                       <td bgcolor="#ffff00"></td>
+                                                       <td bgcolor="#4343bc"></td>
+                                                       <td bgcolor="#797986"></td>
+                                                       <td bgcolor="#4747b8"></td>
+                                               </tr>
+                                               <tr>
+                                                       <td>Turn Propensity</td>
+                                                       <td bgcolor="#2cd3d3"></td>
+                                                       <td bgcolor="#708f8f"></td>
+                                                       <td bgcolor="#ff0000"></td>
+                                                       <td bgcolor="#e81717"></td>
+                                                       <td bgcolor="#a85757"></td>
+                                                       <td bgcolor="#3fc0c0"></td>
+                                                       <td bgcolor="#778888"></td>
+                                                       <td bgcolor="#ff0000"></td>
+                                                       <td bgcolor="#708f8f"></td>
+                                                       <td bgcolor="#00ffff"></td>
+                                                       <td bgcolor="#1ce3e3"></td>
+                                                       <td bgcolor="#7e8181"></td>
+                                                       <td bgcolor="#1ee1e1"></td>
+                                                       <td bgcolor="#1ee1e1"></td>
+                                                       <td bgcolor="#f60909"></td>
+                                                       <td bgcolor="#e11e1e"></td>
+                                                       <td bgcolor="#738c8c"></td>
+                                                       <td bgcolor="#738c8c"></td>
+                                                       <td bgcolor="#9d6262"></td>
+                                                       <td bgcolor="#07f8f8"></td>
+                                                       <td bgcolor="#f30c0c"></td>
+                                                       <td bgcolor="#7c8383"></td>
+                                                       <td bgcolor="#5ba4a4"></td>
+                                               </tr>
+                                               <tr>
+                                                       <td>Buried Index</td>
+                                                       <td bgcolor="#00a35c"></td>
+                                                       <td bgcolor="#00fc03"></td>
+                                                       <td bgcolor="#00eb14"></td>
+                                                       <td bgcolor="#00eb14"></td>
+                                                       <td bgcolor="#0000ff"></td>
+                                                       <td bgcolor="#00f10e"></td>
+                                                       <td bgcolor="#00f10e"></td>
+                                                       <td bgcolor="#009d62"></td>
+                                                       <td bgcolor="#00d52a"></td>
+                                                       <td bgcolor="#0054ab"></td>
+                                                       <td bgcolor="#007b84"></td>
+                                                       <td bgcolor="#00ff00"></td>
+                                                       <td bgcolor="#009768"></td>
+                                                       <td bgcolor="#008778"></td>
+                                                       <td bgcolor="#00e01f"></td>
+                                                       <td bgcolor="#00d52a"></td>
+                                                       <td bgcolor="#00db24"></td>
+                                                       <td bgcolor="#00a857"></td>
+                                                       <td bgcolor="#00e619"></td>
+                                                       <td bgcolor="#005fa0"></td>
+                                                       <td bgcolor="#00eb14"></td>
+                                                       <td bgcolor="#00b649"></td>
+                                                       <td bgcolor="#00f10e"></td>
+                                               </tr>
+                                       </table>
+                               </td>
+                       </tr>
+               </table>
+               <p>&nbsp;</p>
+               <p>
+                       <strong>Nucleotide Colour Schemes</strong>
+               </p>
+               <table border="1">
+                       <tr>
+                               <td>
+                                       <table border="1">
+                                               <tr>
+                                                       <td nowrap></td>
+                                                       <td>A</td>
+                                                       <!--Adenine-->
+                                                       <td>C</td>
+                                                       <!--Cytosine-->
+                                                       <td>G</td>
+                                                       <!--Guanine-->
+                                                       <td>T</td>
+                                                       <!--Thymine-->
+                                                       <td>U</td>
+                                                       <!--Uracil-->
+                                                       <td>I</td>
+                                                       <!--Inosine-->
+                                                       <td>X</td>
+                                                       <!--Xanthine-->
+                                                       <td>R</td>
+                                                       <!--Unknown Purine-->
+                                                       <td>Y</td>
+                                                       <!--Unknown Pyrimidine-->
+                                                       <td>N</td>
+                                                       <!--Unknown-->
+                                                       <td>W</td>
+                                                       <!--Weak nucleotide (A or T)-->
+                                                       <td>S</td>
+                                                       <!--Strong nucleotide (G or C)-->
+                                                       <td>M</td>
+                                                       <!--Amino (A or C)-->
+                                                       <td>K</td>
+                                                       <!--Keto (G or T)-->
+                                                       <td>B</td>
+                                                       <!--Not A (G or C or T)-->
+                                                       <td>H</td>
+                                                       <!--Not G (A or C or T)-->
+                                                       <td>D</td>
+                                                       <!--Not C (A or G or T)-->
+                                                       <td>V</td>
+                                                       <!--Not T (A or G or C-->
+                                               </tr>
+                                               <tr>
+                                                       <td height="24">Nucleotide</td>
+                                                       <td bgcolor="#64F73F"></td>
+                                                       <td bgcolor="#FFB340"></td>
+                                                       <td bgcolor="#EB413C"></td>
+                                                       <td bgcolor="#3C88EE"></td>
+                                                       <td bgcolor="#3C88EE"></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                               </tr>
+                                               <tr>
+                                                       <td height="24">Purine/Pyrimidine</td>
+                                                       <td bgcolor="#FF83FA"></td>
+                                                       <td bgcolor="#40E0D0"></td>
+                                                       <td bgcolor="#FF83FA"></td>
+                                                       <td bgcolor="#40E0D0"></td>
+                                                       <td bgcolor="#40E0D0"></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td bgcolor="#FF83FA"></td>
+                                                       <td bgcolor="#40E0D0"></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                                       <td></td>
+                                               </tr>
+                                       </table>
+                               </td>
+                       </tr>
+               </table>
+
+
+       </div>
+       <p align="center">&nbsp;</p>
+</body>
+</html>
similarity index 90%
rename from help/html/features/clarguments.html
rename to help/help/html/features/clarguments.html
index fa273a5..60db069 100644 (file)
   See
   <a href="commandline.html">running Jalview from the command line</a>
   for more information.
+  <br />
+  <br />Jalview processes arguments on the command line sequentially. If
+  you would like to pass a <a href="jvlfiles.html">'JVL' file</a> containing
+  <a href="../memory.html">memory settings</a> or any other launch
+  parameters, then include it at the beginning of the command line to
+  ensure they are processed before any remaining arguments.
   <br>
   <table width="100%" border="1" cellspacing="0" cellpadding="0">
     <tr>
     </tr>
     <tr>
       <td>
-        <div align="center">-vdoc VAMSAS DOCUMENT FILE/URL</div>
-      <td>
-        <div align="left">
-          Import the given vamsas document into a new session.<br>
-          <em>New in 2.5</em>
-        </div>
-      </td>
-    </tr>
-    <tr>
-      <td>
-        <div align="center">-vsess VAMSAS SESSION URL</div>
-      <td>
-        <div align="left">
-          Join the given vamsas session<br>If a document was also
-          specified, this will be imported first and then committed as
-          new data from Jalview to the specified session (Experimental -
-          not yet enabled!).<em>New in 2.5</em>
-        </div>
-        </div>
-      </td>
-    </tr>
-    <tr>
-      <td>
         <div align="center">-fasta FILE</div>
       </td>
 
diff --git a/help/help/html/features/commandline.html b/help/help/html/features/commandline.html
new file mode 100644 (file)
index 0000000..e00d390
--- /dev/null
@@ -0,0 +1,75 @@
+<html>
+<!--
+ * 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.
+ -->
+<head>
+<title>Running Jalview from the command line</title>
+</head>
+<body>
+  <p>
+    <strong>Running Jalview from the command line</strong>
+  </p>
+  <p>
+    Jalview can be run from the command line, and provides a <a
+      href="clarguments.html">range of arguments</a>. These arguments
+    can also be passed via <strong>.JVL</strong> files, which are opened
+    automatically by the Jalview application when double-clicked in a
+    file browser on Windows and OSX.
+  </p>
+  The way that you launch Jalview from the command line depends on the
+  platform you are on, and how it has been installed.
+
+  <ul>
+    <li>Standard installation on Linux/Unix:<pre>
+       /PATH_TO_JALVIEW/Jalview -open http://www.jalview.org/examples/jpred_msa.fasta -annotations http://www.jalview.org/examples/jpred_msa.seq.concise -colour Clustal</pre>
+    </li>
+    <li>Standard installation on Windows:<pre>
+      \PATH_TO_JALVIEW\Jalview.exe -open %HOMEPATH%\myalignment.fa</pre>
+    </li>
+    <li>OSX Application:<pre>
+open /Applications/Jalview.app --args -open ~/myalignment.fa</pre><em>(put
+        all the Jalview arguments <em>after</em> the --args parameter)
+    </em><br />
+    <br /></li>
+    <li>Jalview standalone executable Jar<pre>
+        &lt;Path to Java home&gt;/bin/java -jar &lt;Path to Jalview Jar&gt;/jalview-all-1.8.jar -open myalignment.fa</pre></li>
+  </ul>
+  <p>
+    If you have installed Jalview via <em>conda</em> or another package
+    manager then you most likely have a 'jalview' command available in
+    your terminal shell's default path. Alternately, if you have built
+    Jalview from source, then take a look at the doc/building.md file
+    included in the source distribution.
+  </p>
+  <p>
+    Use '-help' to get more information on the <a
+      href="clarguments.html">command line arguments</a> that Jalview
+    accepts.
+  </p>
+  <p>
+    <strong>Passing JVM Arguments to Jalview</strong><br /> If you need
+    to modify parameters for Jalview's Java Virtual Machine, then take a
+    look at the instructions for how to <a href="../memory.html#jvm">setting
+      the JVM's maximum memory</a>.
+  </p>
+  <p>&nbsp;</p>
+  <p>&nbsp;</p>
+</body>
+</html>
diff --git a/help/help/html/features/featureDisplaySettings.png b/help/help/html/features/featureDisplaySettings.png
new file mode 100644 (file)
index 0000000..775baad
Binary files /dev/null and b/help/help/html/features/featureDisplaySettings.png differ
diff --git a/help/help/html/features/featureSettings.png b/help/help/html/features/featureSettings.png
new file mode 100644 (file)
index 0000000..e55bb32
Binary files /dev/null and b/help/help/html/features/featureSettings.png differ
similarity index 58%
rename from help/html/features/featuresFormat.html
rename to help/help/html/features/featuresFormat.html
index 4d13dcd..0226175 100755 (executable)
 <body>
   <p>
     <strong>Sequence Features File</strong>
+  
   <p>
-  <p>The Sequence features file (which used to be known as the
-    "Groups file" prior to version 2.08) is a simple way of getting your
-    own sequence annotations into Jalview. It was introduced to allow
-    sequence features to be rendered in the Jalview applet, and so is
-    intentionally lightweight and minimal because the applet is often
-    used in situations where data file size must be kept to a minimum,
-    and no XML parser is available.</p>
+  
+  <p>The Sequence features File provides a simple way of getting
+    your own sequence features into Jalview. It also allows feature
+    display styles and filters to be saved and imported to another
+    alignment. Users familiar with the earliest versions of Jalview will
+    know that features files were originally termed 'groups' files, and
+    that the format was was designed as a space efficient format to
+    allow sequence features to be rendered in the Jalview applet.</p>
 
   <p>
     Features files are imported into Jalview in the following ways:<br>
+  
   <ul>
     <li>from the command line <pre>
 <strong> -features &lt;<em>Features filename</em>&gt;</strong>
     </li>
   </ul>
 
-  </p>
-
   <p>
     <strong>Sequence Features File Format</strong>
   </p>
   <p>
     A features file is a simple ASCII text file, where each line
     contains tab separated text fields. <strong>No comments are
-      allowed</strong>.
+      allowed</strong>. Its structure consists of three blocks:
   </p>
+  <ul>
+    <li><a href="#colourdefs">Feature Colour Specifications</a>
+      define how features of a particular type are rendered.</li>
+    <li><a href="#filterdefs">Feature Filters</a> provide a way of
+      excluding features of a particular type from display and analysis.
+      (new in Jalview 2.11)</li>
+    <li><a href="#featuredef">Sequence Feature definitions</a> -
+      tab separated fields that describe groups of positional and
+      non-positional features. Data can also be provided as <a href="#gff">GFF</a></li>
+  </ul>
+
   <p>
-    <strong>Feature Colours</strong>
+    <strong><a name="colourdefs">Feature Colours</a></strong>
   </p>
   <p>The first set of lines contain feature type definitions and their colours:
   <pre>
-<strong><em>Feature label</em>&#9;<em>Feature Colour</em>
+<strong><em>&lt;Feature Type&gt;</em>&#9;<em>&lt;Feature Style&gt;</em>
 <!-- &#9;<em>Feature links</em>  --></strong>
 </pre>
 
-  A feature type has a text label, and a colour specification. This can
-  be either:
-
-  <ul>
-    <li>A single colour specified as either a red,green,blue 24 bit
+  Each feature type definition assigns a style to features of the given type. &lt;Feature Style&gt; can be either a simple colour, or a more complex <a href="featureschemes.html">Graduated Colour Scheme</a> that shades features according to their description, score, or other attributes.
+<p>
+  <em>Assigning a colour for a &lt;Feature Type&gt;</em><br/>A single colour specified as either a red,green,blue 24 bit
       triplet in hexadecimal (eg. 00ff00) or as comma separated numbers
       (ranging from 0 to 255))<br>
-      (For help with colour values, see <a href="https://www.w3schools.com/colors/colors_converter.asp">https://www.w3schools.com/colors/colors_converter.asp</a>.)</li>
-
-    <li>A <a href="featureschemes.html">graduated colourscheme</a>
-      specified as a "|" separated list of fields: <pre>
-[label <em>or</em> score<em> or</em> attribute|attName|]&lt;mincolor&gt;|&lt;maxcolor&gt;|[absolute|]&lt;minvalue&gt;|&lt;maxvalue&gt;[|&lt;novalue&gt;][|&lt;thresholdtype&gt;|[&lt;threshold value&gt;]]
-</pre> The fields are as follows:
-
-      <ul>
-        <li><em>label</em><br> Indicates that the feature
-          description should be used to create a colour for features of
-          this type.<br> <em>Note: if no threshold value is
-            needed then only 'label' is required.<br> This
-            keyword was added in Jalview 2.6
-        </em></li>
-
-        <li><em>score</em><br> Indicates that the feature
-          score should be used to create a graduated colour for features of
-          this type, in conjunction with mincolor, maxcolor.<br><em>This keyword was added in Jalview 2.11.
-          It may be omitted (score is the default) if mincolor and maxcolor are specified.
-        </em></li>
-
-        <li><em>attribute|attName</em><br> Indicates that the value of feature
-          attribute 'attName' should be used to create a colour for features of
-          this type.
-          <br>For example, <em>attribute|clinical_significance</em> to colour by clinical_significance.
-          <br>To colour by range of a numeric attribute, include <em>mincolor</em> and <em>maxcolor</em>, or omit to colour by text (category).
-          <br>(Note: the value of the attribute used for colouring will also be shown in the tooltip as you mouse over features.)
-          <br>A sub-attribute should be written as, for example, CSQ:IMPACT.
-          <br><em>This keyword was added in Jalview 2.11</em></li>
-
-        <li><em>mincolor</em> and <em>maxcolor</em><br> Colour
-          triplets specified as hexadecimal or comma separated values
-          (may be left blank for a <em>label</em> style colourscheme,
-          but remember to specify the remaining fields)</li>
-
-        <li><em>absolute</em><br> An optional switch
-          indicating that the <em>minvalue</em> and <em>maxvalue</em>
-          parameters should be left as is, rather than rescaled
-          according to the range of scores for this feature type.</li>
-
-        <li><em>minvalue</em> and <em>maxvalue</em><br>
-          Minimum and maximum values defining the range of scores for
-          which the colour range will be defined over.<br>If minvalue is
-          greater than maxvalue then the linear mapping will have
-          negative gradient.</li>
-
-        <li><em>novalue</em> <br>
-          Specifies the colour to use if colouring by attribute, when the attribute is absent.
-          Valid options are <em>novaluemin, novaluemax, novaluenone</em>, to use mincolor, maxcolor, or no colour.
-          <br>If not specified this will default to novaluemin.</li>
-
-        <li><em>thresholdtype</em><br> Either
-          &quot;none&quot;, &quot;below&quot;, or &quot;above&quot;. <em>below</em>
-          and <em>above</em> require an additional <em>threshold
-            value</em> which is used to control the display of features with
-          a score either below or above the value.</li>
-      </ul>
-    </li>
+      (For help with colour values, see <a href="https://www.w3schools.com/colors/colors_converter.asp">https://www.w3schools.com/colors/colors_converter.asp</a>.)</p>
+      <p><em>Specifying a <a href="featureschemes.html">Graduated Colourscheme</a></em><br/>
+      Data dependent feature colourschemes are defined by a series of "|" separated fields: <pre>
+[label <em>or</em> score<em> or</em> attribute|&lt;attName&gt;|]&lt;mincolor&gt;|&lt;maxcolor&gt;|[absolute|]&lt;minvalue&gt;|&lt;maxvalue&gt;[|&lt;novalue&gt;][|&lt;thresholdtype&gt;|[&lt;threshold value&gt;]]
+</pre><br/>The fields are interpreted follows:
+
+  <ul>
+    <li><em>label</em><br> Indicates that the feature
+      description should be used to create a colour for features of this
+      type.<br> <em>Note: if no threshold value is needed then
+        only 'label' is required.<br> This keyword was added in
+        Jalview 2.6
+    </em></li>
+
+    <li><em>score</em><br> Indicates that the feature score
+      should be used to create a graduated colour for features of this
+      type, in conjunction with mincolor, maxcolor.<br>
+    <em>This keyword was added in Jalview 2.11. It may be omitted
+        (score is the default) if mincolor and maxcolor are specified. </em></li>
+
+    <li><em>attribute|&lt;attName&gt;</em><br> Indicates that
+      the value of feature attribute 'attName' should be used to create
+      a colour for features of this type. <br>For example, <em>attribute|clinical_significance</em>
+      to colour by "clinical_significance". <br>To colour by range
+      of a numeric attribute, include <em>mincolor</em> and <em>maxcolor</em>,
+      or omit to colour by text (category). <br>(Note: the value of
+      the attribute used for colouring will also be shown in the tooltip
+      as you mouse over features.) <br>A sub-attribute should be
+      written as, for example, CSQ:IMPACT. <br>
+    <em>This keyword was added in Jalview 2.11</em></li>
+
+    <li><em>mincolor</em> and <em>maxcolor</em><br> Colour
+      triplets specified as hexadecimal or comma separated values (may
+      be left blank for a <em>label</em> style colourscheme, but
+      remember to specify the remaining fields)</li>
+
+    <li><em>absolute</em><br> An optional switch indicating
+      that the <em>minvalue</em> and <em>maxvalue</em> parameters should
+      be left as is, rather than rescaled according to the range of
+      scores for this feature type.<br /> <em>This also enables
+        the 'Threshold is Min/Max' option for this type's feature
+        shading style dialog.</em></li>
+
+    <li><em>minvalue</em> and <em>maxvalue</em><br> Minimum
+      and maximum values defining the range of scores for which the
+      colour range will be defined over.<br>If minvalue is greater
+      than maxvalue then the linear mapping will have negative gradient.</li>
+
+    <li><em>novalue</em> <br> Specifies the colour to use if
+      colouring by attribute, when the attribute is absent. Valid
+      options are <em>novaluemin, novaluemax, novaluenone</em>, to use
+      mincolor, maxcolor, or no colour. <br>If not specified this
+      will default to novaluemin.</li>
+
+    <li><em>thresholdtype</em><br> Either &quot;none&quot;,
+      &quot;below&quot;, or &quot;above&quot;. <em>below</em> and <em>above</em>
+      require an additional <em>threshold value</em> which is used to
+      control the display of features with a score either below or above
+      the value.</li>
   </ul>
-  </p>
 
   <p>
-    <strong>Feature Filters</strong>
+    <strong><a name="filterdefs">Feature Filters</a></strong>
   </p>
   <p>This section is optional, and allows one or more filters to be defined for each feature type.
      <br>Only features that satisfy the filter conditions will be displayed.
   </p>
 
   <p>
-    <strong>Feature Instances</strong>
+    <strong><a name="featuredef">Feature Definitions</a></strong>
   </p>
 
-  <p>The remaining lines in the file are the sequence annotation
-    definitions, where the now defined features are attached to regions
-    on particular sequences. Each feature can optionally include some
-    descriptive text which is displayed in a tooltip when the mouse is
-    near the feature on that sequence (and may also be used to generate
-    a colour for the feature).</p>
+  <p>The remaining lines in the file are sequence feature data.
+    Features are either non-positional - attached to a whole sequence
+    (as specified by its ID), or positional, so attached to a specific
+    range on a sequence. In addition to a type, features can also
+    include descriptive text and a score, and depending on the format
+    used, many additional attributes.</p>
 
+  <em><a name="gff">Importing Generalised Feature Format (GFF) feature data</a></em>
   <p>
-    If your sequence annotation is already available in <a href="http://gmod.org/wiki/GFF2">GFF2</a> (http://gmod.org/wiki/GFF2) or
-    <a href="https://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md">GFF3</a> 
-    (http://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md) format, 
-    then you can leave it as is, after first adding a line containing only
-    'GFF' after any Jalview feature colour definitions (<em>this
-    mixed format capability was added in Jalview 2.6</em>). Alternately,
-    you can use Jalview's own sequence feature annotation format, which
-    additionally allows HTML and URLs to be directly attached to each
-    piece of annotation.
+    Jalview has its own tabular format (described below) for describing
+    sequence features, which allows HTML descriptions (including URLs)
+    to be defined for each feature. However, sequence feature
+    definitions can also be provided in <a
+      href="http://gmod.org/wiki/GFF2">GFF2</a>
+    (http://gmod.org/wiki/GFF2) or <a
+      href="https://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md">GFF3</a>
+    (http://github.com/The-Sequence-Ontology/Specifications/blob/master/gff3.md)
+    format. To do this, a line containing only 'GFF' should precede any
+    GFF data (<em>this mixed format capability was added in Jalview
+      2.6</em>).
   </p>
 
   <p>
-    <strong>Jalview's sequence feature annotation format</strong>
+    <em>Jalview's sequence feature format</em>
   </p>
   <p>Each feature is specified as a tab-separated series of columns
     as defined below:
diff --git a/help/help/html/features/featureschemes.html b/help/help/html/features/featureschemes.html
new file mode 100644 (file)
index 0000000..63dd0e5
--- /dev/null
@@ -0,0 +1,135 @@
+<html>
+<!--
+ * 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.
+ -->
+<head>
+<title>Sequence feature colour schemes</title>
+</head>
+
+<body>
+  <p>
+    <strong>Visualisation of Sequence Features</strong>
+  </p>
+       <p>Jalview can visualize sequence features - the positional
+               annotation present on parts of a sequence, as colours overlaid on
+               corresponding positions in the alignment. Features may be coloured
+               either according to their type, or for a particular type, according to
+               each feature's associated label, score, or other attributes.</p>
+       <p>
+               The <a href="featuresettings.html">Feature Settings dialog box</a>
+               controls the order and display of each feature type, and
+               also allows filters and colour schemes to be configured.
+       </p>
+
+
+       <div align="center">
+               <img src="featureDisplaySettings.png" /><br />
+               <em>Feature Display Settings dialog for 'sequence_variant'
+                       features imported from VCF</em>
+       </div>
+       <p>The Feature Display Settings has the following controls:
+       <p><strong>Colour</strong></p>
+       <ul>
+               <li><em>Simple Colour</em> - select this to assign a given colour
+                       to any features of this type. The assigned colour can be changed by
+                       clicking on the colour box.</li>
+               <li><em>Colour by text</em> - this option derives a colour for
+                       each feature based on its label, or any other attributes presented <em>via</em>
+                       the drop-down menu.</li>
+               <li><em>Graduated Colour</em> - shade features according to
+                       associated score, or other numeric attribute.
+                       <ul>
+                               <li><em>Min and Max Color boxes</em> - Click on these boxes to
+                                       set the minimum and maximum colours for the shading scheme.</li>
+                               <li><em>Threshold type combo box</em> - Specify an exclusion
+                                       threshold.<br />Set the threshold using the slider or
+                                               type it in to the text box. The threshold cannot be set outside
+                                               the available range of values observed for the selected attribute.</li>
+                               <li><em>Threshold is Min/Max</em> - When checked, the threshold
+                                       will be used as the upper or lower limit for shading using the Min
+                                       and Max colour.</li>
+                       </ul></li>
+       </ul>
+       <p>
+               <strong><em>Filters</em></strong><br /> Any number of filter
+               conditions may be specified to select features based on one or more
+               attributes. The <strong>join</strong> condition specifies whether all
+               filters must apply (<strong><em>And</em></strong>) or whether any one
+               filter must apply (<strong><em>Or</em></strong>) for a feature to be
+               selected.
+       </p>
+       <p>The filters available depend on the type of attribute:</p>
+       <ul>
+               <li><strong>Numeric</strong> - specify a threshold above, equal
+                       to or below which a feature will be selected or ignored.</li>
+               <li><strong>String</strong> - select or ignore features based on
+                       presence, absence, equivalence, or containment of a string (not case sensitive).</li>
+       </ul>
+       <p>
+               The filters and colour schemes active for a particular feature type
+               are shown in the <em>Colour</em> and <em>Configuration</em> columns in
+               the feature settings dialog. Styles and filters can be saved as a
+               Jalview Feature Colours file, and are also exported and imported via <a
+                       href="featuresFormat.html">Jalview Feature files</a>.
+       
+       <p>
+               <strong>Icon styles for feature rendering styles</strong><br />
+       
+       <p>
+               When a graduated colour scheme is applied to a feature according to
+               the value of a particular attribute, it is indicated in the feature
+               settings or amend feature dialog box by the following types of icon:<br />
+       
+       <div align="center">
+               <table width="50%" border="1">
+                       <tr>
+                               <td><strong>Type of Colouring</strong></td>
+                               <td><strong>Icon</strong></td>
+                       </tr>
+                       <tr>
+                               <td><em>Graduated colour by Feature Score</em></td>
+                               <td><img src="fcsminmax.gif" /></td>
+                       </tr>
+                       <tr>
+                               <td><em>Graduated colour thresholded (less than) by
+                                               feature Score (or other attribute)</em></td>
+                               <td><img src="fcsltthan.gif" /></td>
+                       </tr>
+                       <tr>
+                               <td><em>Graduated colour thresholded (greater than) by
+                                               feature Score (or other attribute)</em></td>
+                               <td><img src="fcsgtthan.gif" /></td>
+                       </tr>
+                       <tr>
+                               <td><em>Colour by Feature Label</em></td>
+                               <td><img src="fcsntlabel.gif" /></td>
+                       </tr>
+                       <tr>
+                               <td align="center" colspan="2"><em> <strong>The
+                                                       current threshold and attribute is shown in the icon's tooltip.</strong>
+                               </em></td>
+                       </tr>
+               </table>
+       </div>
+       </p>
+       <em>Graduated feature colours were introduced in Jalview 2.5.
+               Attribute based colouring and filters were added in Jalview 2.11.</em>
+</body>
+</html>
similarity index 52%
rename from help/html/features/featuresettings.html
rename to help/help/html/features/featuresettings.html
index 3d7c944..555e2ba 100755 (executable)
   <p>
     <strong>Sequence Feature Settings Dialog Box</strong>
   </p>
+       <p>
+               Select <strong>View&#8594;Feature Settings...</strong> in an alignment
+               window to open the feature settings dialog box, which allows
+               precise control of the visibility and appearance of sequence features for
+               an alignment view.
+       </p>
+       <div align="center">
+    <img src="featureSettings.png" width="400"> <br/>
+    Sequence Feature Settings for the Jalview Application (version 2.11)
+  </div>
+       <p>The top section of the dialog box lists all the sequence feature
+               groups, along with a tickbox for each that controls whether its
+               features are displayed. When many groups are present, their names may
+               be truncated, but the full name is shown in a tooltip when the mouse
+               hovers over their checkbox.</p>
+               <p><em>Feature colour schemes and filters</em>
+       <p>
+               The table in the middle lists all the feature types in the currently
+               selected groups, along with their display style, any configured
+               filters, and a tickbox indicating whether the type is enabled for
+               display.</p><p>Features may be highlighted with either a single colour or a <a
+      href="featureschemes.html">feature colourscheme</a> based on
+    either the scores associated with that feature or from the feature's
+    description (e.g. to distinguish different names associated with a
+    DOMAIN feature).The colour used for a feature can
+                               be changed by clicking on its <strong><em>colour box</em></strong>. To configure the shading
+                               style or filter for a feature, click its <strong><em>configuration</em></strong> box.
+       </p>
+         <p>
+    <em>Feature settings pop-up menu</em><br> <strong>Right-click</strong>
+    on a feature to open a pop-up menu that allows you to
+  <ul>
+    <li>Hide, show and select columns containing that feature</li>
+    <li>Sort the alignment or current selection according to count or score for that feature
+      type (see below)</li>
+  </ul>
+
+       <p>
+               <strong>Transparency and Feature Ordering</strong>
+       </p>
+       <p>Sequence features represent a variety of different types of
+               positional annotation, and often overlap. For example, a metal binding
+               site feature may be attached to one position along a stretch of
+               sequence marked with a secondary structure feature.</p>
+       <p>
+    The ordering of the sequence features in the dialog box list is the
+    order used by Jalview for rendering sequence features. A feature at
+    the bottom of the list is rendered <em>below</em> a feature higher
+    up in the list.<br> <em><strong>You can change
+        the order of a feature by dragging it up and down the list with
+        the mouse.</strong></em>
+  </p>
+       <p>
+               Use the <strong><em>Optimise order</em></strong> button to adjust the
+               feature render ordering to place features that on average cover shorter regions
+               of sequence above features annotating longer regions.
+       </p>
+       <p><a name="transparency"></a>
+    The <strong><em>transparency slider</em></strong> controls
+    the visibility of features rendered below other features. Reducing
+    the transparency will mean that features at the top of the list can
+    obscure features lower down, and increasing it allows the user to
+    'see through' the upper layers of a set of features.
+  </p>
   <p>
-    Select <strong>View&#8594;Feature Settings...</strong> menu entry in
-    an alignment window to open the feature settings dialog box, which
-    allows you to precisely control the presence and appearance of
-    sequence features for the current alignment.
+    <strong><em>You can save all features, with their
+        current colours and visibility in a Jalview format file.  </em></strong>
   </p>
-  <center>
-    <img src="featureSettings.gif" width="400" height="452"> <br>
-    Sequence Feature Settings for the Jalview Application</img>
-  </center>
   <p>
-    The top section of the dialog box lists all the sequence feature
-    groups, along with a tickbox for each that controls whether its
-    features are displayed. The table in the middle lists all the
-    features in the currently selected groups, along with their display
-    style and whether they are currently being displayed (only the
-    ticked features and groups are displayed). <strong><em>You
-        can change the colour or shading style used for a feature in the
-        associated alignment by clicking on its colour box.</em></strong>
+    <a name="selectbyfeature"><strong>Selecting alignment
+        columns by feature</strong></a>
   </p>
   <p>
-    <a name="selectbyfeature"><strong><em>Selecting
-          alignment columns by feature</em></strong></a><br> <strong>Double-clicking
-      a feature type</strong> in the <strong>Feature Settings</strong> dialog
-    allows you to select columns in the alignment that contain (or do
-    not contain) features of that type. If a region of the alignment is
-    currently selected, then only features in the current selection will
-    be searched. The following keys affect the way in which selections
-    are made:
-  <ul>
+               Double-clicking a <strong>feature type</strong> in the <strong>Feature
+                       Settings</strong> dialog allows you to select columns in the
+               alignment that contain (or do not contain) features of that type that
+               are currently visible (e.g. not filtered out or otherwise excluded due
+               to an applied colour scheme threshold). If a region of the alignment
+               is currently selected, then only features in the current selection
+               will be searched. The following keys affect the way in which
+               selections are made:<ul>
     <li>Hold down <strong>Alt</strong> to select columns not
       containing features of a particular type.
     </li>
   <em>Select columns by feature was added in Jalview 2.8.1</em>
   </p>
   <p>
-    <strong><em>Feature settings pop-up menu</em></strong><br> <strong>Right-click</strong>
-    on a feature to open a pop-up menu that allows you to
-  <ul>
-    <li>Hide, show and select columns containing that feature</li>
-    <li>Sort the alignment or current selection using that feature
-      type (see below)</li>
-    <li>Toggle the type of colouring used for the feature</li>
-  </ul>
-  <p>
-    Features may be highlighted with either a single colour or a <a
-      href="featureschemes.html">feature colourscheme</a> based on
-    either the scores associated with that feature or from the feature's
-    description (e.g. to distinguish different names associated with a
-    DOMAIN feature).
-  </p>
-  <p>
     <strong><a name="sortbyfeature">Ordering alignment by
-        features</a></strong><br> The 'Seq Sort by Score' and 'Seq Sort by
+        features</a></strong></p><p>The 'Seq Sort by Score' and 'Seq Sort by
     Density' buttons will sort the alignment based on the average score
     or total number of currently active features and groups on each
     sequence. To order the alignment using a specific feature type, use
       introduced in Jalview 2.5</em>
   </p>
 
-  <p>
-    <strong>Transparency and Feature Ordering</strong>
-  </p>
-  <p>It is important to realise that sequence features are often not
-    distinct and often overlap (for example, a metal binding site
-    feature may be attached to one position along a stretch of sequence
-    marked with a secondary structure feature).</p>
-  <p>
-    The ordering of the sequence features in the dialog box list is the
-    order used by Jalview for rendering sequence features. A feature at
-    the bottom of the list is rendered <em>below</em> a feature higher
-    up in the list.<br> <em><strong>You can change
-        the order of a feature by dragging it up and down the list with
-        the mouse (not applet)</strong></em>.
-  </p>
-  <p>
-    The <strong><em>Optimise order</em></strong> button (currently only
-    available in the application) will re-order the feature render
-    ordering based on the average length of each feature type.
-  </p>
-  <p>
-    The <strong><em>transparency slider setting</em></strong> controls
-    the visibility of features rendered below other features. Reducing
-    the transparency will mean that features at the top of the list can
-    obscure features lower down, and increasing it allows the user to
-    'see through' the upper layers of a set of features.
-  </p>
-  <p>
-    <strong><em>You can save all features, with their
-        current colours and visibility in a Jalview format file. </em></strong>
-  </p>
 </body>
 </html>
diff --git a/help/help/html/features/importvcf.html b/help/help/html/features/importvcf.html
new file mode 100755 (executable)
index 0000000..e013472
--- /dev/null
@@ -0,0 +1,74 @@
+<html>
+<!--
+ * 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.
+ -->
+<head>
+<title>Importing Variants from VCF</title>
+</head>
+<body>
+  <p>
+    <strong>Importing Genomic Variants from VCF</strong>
+  </p>
+
+  <p>Jalview can annotate nucleotide sequences associated with
+    genomic loci with features representing variants imported from VCF
+    files. This new feature in Jalview 2.11, is currently tuned to work
+    best with tab indexed VCF files produced by the GATK Variant
+    Annotation Pipeline (with or without annotation provided by the
+    Ensembl Variant Effect Predictor), but other sources of VCF files
+    should also work.</p>
+  <p>
+    If your sequences have genomic loci, then a <strong>Taxon
+      name</strong> and <strong>chromosome location</strong> should be shown in
+    the Sequence Details report and the Sequence ID tooltip (providing
+    you have enabled it via the submenu in the <em><strong>View</strong></em>
+    menu). Jalview matches the assembly information provided in the VCF
+    file to the taxon name, using an internal lookup table. If a match
+    is found, Jalview employs the Ensembl API's lift-over services to
+    locate your sequences' loci in the VCF file assembly's reference
+    frame. If all goes well, after loading a VCF, Jalview will report
+    the number of variants added as sequence features via the alignment
+    window's status bar. These are added by default when loci are
+    retrieved from Ensembl.
+  </p>
+  <p>
+    <strong>Working with variants from organisms other than
+      H.sapiens.</strong>
+  </p>
+  <ol>
+    <li>Look in your VCF file to identify keywords in the
+      ##reference header that define what species and assembly name the
+      VCF was generated against.</li>
+    <li>Look at ensembl.org to identify the species' short name,
+      and the assembly's unique id.</li>
+    <li>Add mappings to the <strong>VCF_SPECIES</strong> and <strong>VCF_ASSEMBLY</strong>
+      properties in your .jalview_properties file. For example:<pre>
+VCF_SPECIES=1000genomes=homo_sapiens,c_elegans=celegans
+VCF_ASSEMBLY=assembly19=GRCh37,hs37=GRCh37</pre><br /> <br />These allow
+      annotations to be mapped from both Human 1000genomes VCF files and
+      C.elegans files.
+    </li>
+  </ol>
+  <strong>Work in Progress!</strong>
+  <p>VCF support in Jalview is under active development. Please get
+    in touch via our mailing list if you have any questions, problems or
+    otherwise find it useful !</p>
+</body>
+</html>
diff --git a/help/help/html/features/jalarchive.html b/help/help/html/features/jalarchive.html
new file mode 100755 (executable)
index 0000000..ef6cb4f
--- /dev/null
@@ -0,0 +1,50 @@
+<html>
+<!--
+ * 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.
+ -->
+<head>
+<title>Jalview Archives</title>
+</head>
+<body>
+
+  <p>
+    <strong>Jalview Project Archives</strong>
+  </p>
+
+  <p>These are Zip archives of an XML file containing alignments,
+    trees, Principal Component Analyses (since 2.11.0), 3D structures,
+    and display information for Jalview, VARNA, Jmol and UCSF Chimera.
+    XML documents conform to a schema, and changes to the format are
+    made to ensure future versions of Jalview will be able to read old
+    Jalview project files.</p>
+  <p>
+    <em>For those who want to know...</em><br> Jalview 2.11.0
+    onwards employ the JAX-B java to XML binding framework to read and
+    write Jalview project files. Earlier versions of Jalview employed <a
+      href="http://www.castor.org">Castor</a>. Jalview 2.08 used
+    descriptor classes which significantly increase the speed of
+    marshalling / unmarshalling java objects into XML. <br /> <br />
+    Files created prior to Jalview 2.08 can no longer be read by Jalview
+    2.11.0. If you need to import old project files, then please import
+    them to Jalview 2.10.5 (available from the Jalview version archive)
+    and save the project again to convert it to the new format.
+  </p>
+</body>
+</html>
diff --git a/help/help/html/features/jvlfiles.html b/help/help/html/features/jvlfiles.html
new file mode 100644 (file)
index 0000000..27742b3
--- /dev/null
@@ -0,0 +1,70 @@
+<html>
+<!--
+ * 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.
+ -->
+<title>JVL - Jalview Launch Files</title>
+<body>
+  <p>
+    <strong>Jalview Launch Files - JVL</strong>
+  </p>
+  <p>Files with a '.jvl' (or .JVL) extension can be opened by the
+    Jalview desktop application, and allow you to:</p>
+  <ul>
+    <li><strong>Switch between different Jalview Versions</strong></li>
+    <li><strong>Configure the <a href="../memory.html">proportion
+          of memory</a> allocated to Jalview
+    </strong></li>
+    <li><strong>Import data and pass <a
+        href="clarguments.html">command line arguments</a></strong></li>
+  </ul>
+  <p>
+    Some example JVL files are given below - copy and paste them into a
+    text editor and save them as a file with the extension '.jvl'. To
+    test, open your saved .jvl file via the OSX File Chooser or Windows
+    file explorer, or pass it to Jalview via the <a
+      href="./commandline.html">command line</a>
+  </p>
+  <p>
+    <strong>Passing command line arguments</strong>
+  <pre>
+# Jalview Launch File
+# Please install the Jalview Desktop from 
+# http://www.jalview.org/getdown/release
+# and then try to open this file again
+jalview.apparg=-open
+jalview.apparg=http://www.jalview.org/examples/jpred_msa.fasta
+jalview.apparg=-annotations
+jalview.apparg=http://www.jalview.org/examples/jpred_msa.seq.concise
+jalview.apparg=-colour
+jalview.apparg=Clustal
+</pre>
+  <p>
+    <strong>Launching a different version of Jalview</strong>
+  <pre>
+# Jalview Launch File
+# Please install the Jalview Desktop from 
+# http://www.jalview.org/getdown/release
+# and then try to open this file again
+appbase=http://www.jalview.org/getdown/archive/2_10_5/
+</pre>
+  For security, the Jalview application will only allow
+  <em>appbase</em> URLs from www.jalview.org.
+</body>
+</html>
similarity index 90%
rename from help/html/features/preferences.html
rename to help/help/html/features/preferences.html
index 8c6b699..5fda2df 100755 (executable)
     overview that are hidden in the alignment.
   </p>
   <p>
-    <em>Gap Colour</em> - The default colour scheme for a new alignment
-    window. If the chosen option is &quot;User Defined&quot; then the
-    last User Defined Colour loaded or saved via the User Defined
-    Colours panel will be loaded.
-  </p>
-  <p>
     <a name="structure"><strong>&quot;Structure&quot;
         Preferences tab</strong></a><em> added in Jalview 2.8.2</em>
   </p>
     here, by entering the full path to the Chimera executable program.
     Double-click this field to open a file chooser dialog.
   <p>
+  <p>
+    <em>PDB Fields shown in Search and Structure Summaries</em> - ticks
+    in this table indicate fields shown by default when browsing results
+    of a free text search via the PDB sequence fetcher, or 3D structures
+    offered by the 3D Structure Chooser.<p>
+  <p>
     <a name="connections"><strong>&quot;Connections&quot;
         Preferences tab</strong></a>
   </p>
       statement</a> for more information.
   </p>
   <p>
+  <a name="backups"><strong>The &quot;Backups&quot; Preferences Tab</strong></a>
+  </p>
+  <p>Since Jalview 2.11.0, overwriting an existing file when saving
+    or exporting data will by default trigger a backup file to be
+    created. Several options are provided to control how backup files
+    are named, and how many old versions will be kept.</p>
+    <p>
+    <em>Schemes</em> - select from three default schemes <em>Single Backup</em>, <em>Keep All Versions</em>, and <em>Rolled Backup Files</em>, or choose <em>Custom</em> to enable a previously defined custom scheme.
+    </p><p>
+    <em>Custom</em> - Check this box to adjust the parameters of the currently selected scheme. Once <em>OK</em> is selected these parameters will be saved as the <em>Custom</em> scheme in your user preferences. To revert changes hit <em>Cancel</em>. 
+    </p>
+    <p><em>Scheme Examples</em> - shows how backup files will appear according to the currently selected scheme and parameters. 
+    </p>
+    <p><em>Deleting Old Backup Files</em> - these settings control how many backups are kept.</p>
+    <p><em>Backup filename strategy</em> - specify the naming convention for numbered backups and how they are ordered.</p> 
+  <p>
     <a name="links"><strong>The &quot;Links&quot; Preferences
         tab</strong></a>
   </p>
     loaded onto the alignment will automatically sort the alignment.
   </p>
   <p>&nbsp;</p>
+  <em>Web Services Preferences</em> - documentation for this tab is
+  given in the
+  <a href="../webServices/webServicesPrefs.html">Web Services
+    Preferences section</a>.
   <p>&nbsp;</p>
 </body>
 </html>
similarity index 95%
rename from help/html/io/export.html
rename to help/help/html/io/export.html
index 8a554aa..04216da 100755 (executable)
@@ -82,6 +82,8 @@
     blurred. Right-click the image and choose &quot;Edit image.&quot; to
     convert it to word drawing objects which give a truer WYSIWIG
     representation.</li>
+  <li>EPS does not support colour transparency, so will not reproduce this
+    if used for <a href="../features/featuresettings.html#transparency">feature colouring</a>.</li>
   <li>Mac OSX users will find that eps files are automatically
     converted into PDF files.</li>
   </p>
similarity index 100%
rename from help/html/keys.html
rename to help/help/html/keys.html
diff --git a/help/help/html/memory.html b/help/help/html/memory.html
new file mode 100755 (executable)
index 0000000..4baebf2
--- /dev/null
@@ -0,0 +1,133 @@
+<html>
+<!--
+ * 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.
+ -->
+<head>
+<title>Memory Settings</title>
+</head>
+<body>
+  <h2>
+    <center>
+      <strong>Memory Usage Settings for Jalview</strong>
+    </center>
+  </h2>
+  <p>When launched as an Application, Jalview will automatically
+    configure the amount of memory allocated to the program to be 90% of
+    physical memory. You may wish to change this behaviour -
+    particularly if you are working on a machine that runs other memory
+    intensive processes.
+  <p>
+    <em>Signs that Jalview is Running out of Memory</em><br /> If
+    Jalview has not explicitly told you that it has run out of memory,
+    then a common sign is that a function that normally works seems to
+    have no effect when working with a larger set of sequences (this
+    might include open dialog boxes for saving PNG files, or when
+    interpreting the result of a web service calculation).
+  </p>
+  <p>
+    <em>Jalview Memory Usage Monitor</em>: If you are concerned about
+    memory, or think that things might be behaving strangely because of
+    a shortage of memory, then you can check this by enabling the memory
+    usage monitor. This is done by selecting the <strong>Tools&#8594;Show
+      Memory Usage</strong> option. Once enabled, the memory usage monitor
+    displays the currently available memory, the total memory, and the
+    percentage free at the bottom left hand side of the Jalview Desktop
+    window's background.
+  </p>
+  <p>
+    <em>Increasing the memory available to Jalview</em><br /> The
+    amount of memory allocated is defined wheb Jalview is launched
+    because of the way that Java runs on a computer - what is actually
+    run is a program called a Java virtual machine (a JVM) which
+    executes the java program instructions. The JVM has limits on the
+    memory that can be allocated to the java program - and it is often
+    necessary to adjust them if you are working with particularly large
+    datasets, or need to make room for other processes on the machine.<br />
+    <br />Jalview 2.11 includes a launcher that automatically
+    configures the proportion of memory allocated to Jalview's JVM, and
+    its behaviour can be altered in a number of different ways:
+  </p>
+
+  <ul>
+    <li><em><font size="3">JVL file</font></em> <br /> The easiest
+      way to launch Jalview with a different percentage of physical
+      memory available is to create a text file with extension <em>.jvl</em>
+      and a single line to specify the percentage of memory you wish
+      Jalview to request: <pre>
+      jalview.jvmmempc=50</pre> In Windows and in macOS you can then launch Jalview by
+      double clicking on this file, and your memory setting will be used
+      instead of the default value. <br /> <br /> In Linux or other
+      unix variants you can launch Jalview on the command line and
+      provide your JVL file as an argument with <pre>
+      /PATH_TO_JALVIEW/Jalview /path/to/file/mymemorysetting.jvl</pre> If you want to use a memory setting like this and open a
+      file you can use both the jvl and alignment files as command line
+      arguments, but you must put the <em>jvl</em> file first, e.g. <pre>
+      /PATH_TO_JALVIEW/Jalview /path/to/file/mymemorysetting.jvl /path/to/alignments/myalignment.fa</pre> Alternatively, you can use the standard Jalview command line
+      arguments with or without the jvl file (first), e.g. <pre>
+       /PATH_TO_JALVIEW/Jalview /path/to/file/mymemorysetting.jvl -open http://www.jalview.org/examples/jpred_msa.fasta -annotations http://www.jalview.org/examples/jpred_msa.seq.concise -colour Clustal</pre> You can use command line arguments to control memory
+      settings in Windows and macOS too: <br /> In Windows you must
+      use, e.g. <pre>
+      \PATH_TO_JALVIEW\Jalview.exe %HOMEPATH%\mymemorysetting.jvl -open %HOMEPATH%\myalignment.fa</pre> In macOS you can use the macOS <em>open</em> command like this: <pre>
+      open /Applications/Jalview.app --args ~/mymemorysetting.jvl -open ~/myalignment.fa</pre><em>(put all the Jalview arguments <em>after</em> the --args
+        parameter)
+    </em><br/><br/></li>
+    <li><em><font size="3"><a name="jvm"/>Directly opening Jalview
+          with a JVM</font></em> <br /> Launching Jalview directly with a JVM is
+      entirely possible, but is not recommended for regular interactive
+      use because it bypasses Jalview's launcher which also handles
+      automatic updates and configuration of other aspects of Jalview
+      operation. <br /> However by launching Jalview in this way you
+      have full access to the Java command line arguments. In particular
+      you can set the maximum allowed memory with the <em>-Xmx...</em>
+      JVM argument. <br /> <em>-Xmx</em> should be immediately followed
+      (no space or equals) by the maximum amount of memory specified in
+      bytes, or in kilobytes, megabytes or gigabytes by following the
+      number with a "k", "m" or "g" respectively. <br />For example: <pre>
+      -Xmx8g</pre>Jalview binaries for Windows and macOS are distributed
+      with their own JVM which you will find in
+      <ul>
+        <li><em>Windows:</em> .../Jalview/jre/bin/java.exe</li>
+        <li><em>macOS:</em>
+          .../Jalview.app/Contents/Resources/app/jre/Contents/Home/bin/java</li>
+      </ul> For linux and other unixes you will have to install a Java 1.8
+      JRE (we recommend the ones found at <a
+      href="https://adoptopenjdk.net">https://adoptopenjdk.net/</a>) <br />
+      <br /> You will also need to reference the "appdir" release
+      folder with all of the Jalview jar files.
+      <ul>
+        <li>On Windows this will be <pre>\PATH_TO_JALVIEW\release</pre>
+        </li>
+        <li>On macOS it will be <pre>/Applications/Jalview.app/Contents/Resources/app/release</pre>
+          and on linux or unix <pre>/PATH_TO_JALVIEW/release</pre>
+        </li>
+      </ul> Assuming the <em>java</em> (or <em>java.exe</em> on Windows)
+      commands are available to you, you can run, e.g. <pre>
+      java -Xmx1500m -cp "/PATH_TO_RELEASE_DIR/*" jalview.bin.Jalview
+      </pre> Or on Windows <pre>
+      java.exe -Xmx1500m -cp "\PATH_TO_RELEASE_DIR\*" jalview.bin.Jalview
+      </pre> <em>Note:</em> for this to work the classpath argument wildcard <strong>must</strong> be simply
+      a '*' and not '*.jar'. <br /> <br />
+      You can also add other <a href="features/commandline.html">Jalview
+        command line arguments</a> as above after the <em>jalview.bin.Jalview</em>
+      class name, but <strong>you cannot use <em>jvl</em> files
+    </strong> if launching Jalview in this way.</li>
+  </ul>
+</body>
+</html>
similarity index 99%
rename from help/html/menus/alignmentMenu.html
rename to help/help/html/menus/alignmentMenu.html
index 3a057a8..e26707f 100755 (executable)
     <li><strong>Calculate Tree or PCA ...</strong><em> <br> Opens the 
     <a href="../calculations/calculations.html">calculations dialog</a> for
         for calculating <a href="../calculations/tree.html">trees</a> or
-         <a href="../calculations/pca.html">principle component analysis 
+         <a href="../calculations/pca.html">principal component analysis 
          plots</a> on the alignment or the currently selected
         region. 
       </em><br></li>
similarity index 91%
rename from help/html/menus/alwcalculate.html
rename to help/help/html/menus/alwcalculate.html
index c7a1c87..3804538 100755 (executable)
@@ -56,7 +56,7 @@
     <li><strong>Calculate Tree or PCA ...</strong> <br> <em>Opens the 
     <a href="../calculations/calculations.html">calculations dialog</a> for
         for calculating <a href="../calculations/tree.html">trees</a> or
-         <a href="../calculations/pca.html">principle component analysis 
+         <a href="../calculations/pca.html">principal component analysis 
          plots</a> on the alignment or the currently selected
         region. 
       </em><br>
         parsed into sequence associated annotation which can then be
         used to sort the alignment via the Sort by&#8594;Score menu.
     </em> <br></li>
-    <li><strong>Translate as cDNA</strong> (not applet)<br> <em>This
+    <li><a name="transcdna"><strong>Translate as cDNA</strong></a><br> <em>This
         option is visible for nucleotide alignments. Selecting this
         option shows the DNA's calculated protein product in a new <a
         href="../features/splitView.html">split frame</a> window. Note
         that the translation is not frame- or intron-aware; it simply
-        translates all codons in each sequence, using the standard <a
-        href="../misc/geneticCode.html">genetic code</a> (any incomplete
-        final codon is discarded). You can perform this action on the
-        whole alignment, or selected rows, columns, or regions.
+        translates all codons in each sequence. You can use the standard <a
+        href="../misc/geneticCode.html">genetic code</a>, or choose an 
+        alternative code table (any incomplete final codon is discarded). 
+        You can perform this action on the whole alignment, or selected 
+        rows, columns, or regions. Alternative code tables were added in
+        Jalview 2.11.
     </em> <br></li>
     <li><strong>Reverse, Reverse Complement</strong> (not applet)<br>
       <em>These options are visible for nucleotide alignments.
similarity index 95%
rename from help/html/menus/alwfile.html
rename to help/help/html/menus/alwfile.html
index b8445af..653eeda 100755 (executable)
           features</a> or <a href="../features/annotationsFormat.html">alignment
           annotations</a>.
     </em></li>
+    <li><strong>Load VCF File<br>
+    </strong><em><a href="../features/importvcf.html">Load VCF annotations</a> from a plain text, or indexed file (.csi,.tsi).
+    <br>Only available for nucleotide alignments, and requires at least one
+    sequence to have known genomic coordinates.</em></li>
     <li><strong>Close (Control W)</strong><br> <em>Close
         the alignment window. Make sure you have saved your alignment
         before you close - either as a Jalview project or by using the <strong>Save
similarity index 80%
rename from help/html/menus/desktopMenu.html
rename to help/help/html/menus/desktopMenu.html
index d716e33..49fbec3 100755 (executable)
         <!--         <li><strong>Enable Experimental Features</strong> <em>Enable or disable <a href="../whatsNew.html#experimental">features still under development</a> in Jalview's user interface. This setting is remembered in your preferences.</em> -->
 
       </ul></li>
-    <li><strong>Vamsas</strong> <em>For more details, read the
-        <a href="../vamsas/index.html">Jalview VAMSAS documentation</a>.<br />
-        When no session is active, the menu provides options for
-        initiating a new VAMSAS session:
-    </em>
-      <ul>
-        <li><strong>Connect to<br /></strong> <em>Connect to
-            one of the existing sessions available.</em></li>
-        <li><strong>New VAMSAS session<br /></strong> <em>Initiates
-            a new local VAMSAS session.</em></li>
-        <li><strong>Load VAMSAS session...<br /></strong> <em>Opens
-            a file browser allowing you to choose a VAMSAS document to
-            import into a new local session.</em></li>
-      </ul> <em>When Jalview is joined to a VAMSAS session, the menu
-        contains the following:</em>
-      <ul>
-        <li><strong>Session Update<br /></strong> <em>Sends
-            alignment data in the Jalview desktop to the VAMSAS session.</em></li>
-        <li><strong>Save VAMSAS Session...<br /></strong> <em>Saves
-            the current session to a VAMSAS document archive.</em></li>
-        <li><strong>Stop VAMSAS Session<br /></strong> <em>Disconnects
-            from the current session. You may be asked if you want to
-            save the current session's data if no other VAMSAS clients
-            are connected.</em></li>
-      </ul></li>
     <li><strong>Help </strong>
       <ul>
         <li><strong>About<br>
similarity index 95%
rename from help/html/privacy.html
rename to help/help/html/privacy.html
index 37ae169..9ad61b5 100644 (file)
@@ -40,9 +40,9 @@
       via the browser when downloading the application, or when the
       Jalview Desktop user interface is launched.<br> <br>
       <ul>
-        <li><i>The JNLP file at
-            www.jalview.org/webstart/jalview.jnlp is retrieved to
-            determine if you are running the latest version of Jalview.</i></li>
+        <li><i>The Jalview Getdown Launcher</i> (Since 2.11.0) examines release
+          channels every time Jalview launches to determine if a new
+          release is available.</li>
         <li><i>The questionnaire web service at
             www.jalview.org/cgi-bin/questionnaire.pl is checked and a
             unique cookie for the current questionnaire is stored in the
similarity index 90%
rename from help/html/releases.html
rename to help/help/html/releases.html
index 3ac1247..0a20650 100755 (executable)
@@ -51,158 +51,487 @@ li:before {
   </p>
   <table border="1">
     <tr>
-      <td width="60" nowrap>
-        <div align="center">
-          <em><strong>Release</strong></em>
-        </div>
-      </td>
-      <td>
-        <div align="center">
-          <em><strong>New Features</strong></em>
-        </div>
-      </td>
-      <td>
-        <div align="center">
-          <em><strong>Issues Resolved</strong></em>
-        </div>
-      </td>
+      <th nowrap><em>Release</em></th>
+      <th><em>New Features</em></th>
+      <th><em>Issues Resolved</em></th>
     </tr>
-    
-      <td width="60" nowrap>
-        <div align="center">
-          <strong><a name="Jalview.2.11">2.11</a><br />
-            <em>14/05/2019 (final due date !)</em></strong>
-        </div>
+    <tr>
+      <td width="60" align="center" nowrap>
+          <strong><a name="Jalview.2.11.0">2.11.0</a><br />
+            <em>04/07/2019</em></strong>
       </td>
-    <td><div align="left">
+      <td align="left" valign="top">
         <ul>
           <li>
-            <!-- JAL-1767,JAL-2647 -->Principal Components Analysis and
-            Viewer state is saved in Jalview Project<br />The 'Change
-            parameters' option has also been removed from the PCA
-            viewer.
+            <!-- JAL-1059, JAL-3196,JAL-3007,JAL-3236 -->Jalview Native Application and
+            Installers built with <a href="https://www.ej-technologies.com/products/install4j/overview.html">install4j</a> (licensed to the Jalview open
+            source project) rather than InstallAnywhere
           </li>
           <li>
-            <!-- JAL-3198 -->More efficient creation of selections and
-            multiple groups when working with large alignments
+            <!-- JAL-1929 -->Jalview Launcher System to auto-configure memory
+            settings, receive over the air updates and launch specific
+            versions via (<a href="https://github.com/threerings/getdown">Three
+              Rings' GetDown</a>)
           </li>
+                                       <li>
+                                               <!-- JAL-1839,JAL-3254,JAL-3260 -->File type associations for
+                                               formats supported by Jalview (including .jvp project files)
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3260 -->Jalview launch files (.jvl) to pass command line
+                                               arguments and switch between different getdown channels
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3141 -->Backup files created when saving Jalview project
+                                               or alignment files
+                                       </li>
+
+                                       <li>
+            <!-- JAL-1793 -->Annotate nucleotide alignments from VCF data files</li>
+            <li><!-- JAL-2753 -->Version of HTSJDK shipped with Jalview updated to version 2.12.0</li> 
           <li>
-            <!-- JAL-3200 -->Speedier import of annotation rows when
-            parsing stockholm files
-          </li>
+            <!-- JAL-2620 -->Alternative genetic code tables for
+            'Translate as cDNA'</li>
           <li>
-            <!-- JAL-3018 -->Update of Ensembl Rest Client to API v9.0
-          </li>
-          <li>
-            <!-- JAL-3... -->Sequence features can be filtered and
-            shaded according to any associated attributes (e.g. variant
-            attributes from VCF file, or key-value pairs imported from
-            column 9 of GFF file)
-          </li>
+            <!-- JAL-3018 -->Update of Ensembl Rest Client to API v10.0</li>
+                                       <li><strong>Enhanced visualisation and analysis of Sequence Features</strong>
+                                               <ul>
+                                                         <li>
+            <!-- JAL-3140 JAL-2446 -->IntervalStoreJ (NCList
+            implementation that allows updates) used for Sequence Feature collections</li>
           <li>
-            <!-- JAL-3139, -->More efficient sequence feature render
-            algorithm (Z-sort/transparency and filter aware)
-          </li>
-          <li>
-            <!-- JAL-2527 -->Alignment Overview now by default shows
-            only visible region of alignment (this can be changed in
-            user preferences)
-          </li>
-          <li><!--  -->
-          </li>
-        </ul>
+                <!-- JAL-2744, JAL-2808,JAL-2069,JAL-2820 -->Sequence
+                features can be filtered and shaded according to any
+                associated attributes (e.g. variant attributes from VCF
+                file, or key-value pairs imported from column 9 of GFF
+                file)
+              </li>
+              <li>
+                <!-- JAL-2879 -->Feature Attributes and shading schemes
+                stored and restored from Jalview Projects
+              </li>
+              <li>
+                                                               <!-- JAL-3334 -->Use full Sequence Ontology (via BioJava) to
+                                                               recognise variant features
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-2897,JAL-3330 -->Show synonymous codon variants on peptide
+                                                               sequences (also coloured red by default)
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-2792 -->Popup window to show full report for a selected sequence feature's
+                                                               details
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-3139,JAL-2816,JAL-1117 -->More efficient sequence feature render
+                                                               algorithm (Z-sort/transparency and filter aware)
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-3049,JAL-3054 -->Improved tooltips in Feature Settings
+                                                               dialog
+                                                       </li>
+                                               </ul>
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3205 -->Symmetric score matrices for faster
+                                               tree and PCA calculations
+                                       </li>
+                                       <li><strong>Principal Components Analysis Viewer</strong>
+            <ul>
+                                                       <li>
+                                                               <!-- JAL-1767,JAL-2647 -->Principal Components Analysis results
+                                                               and Viewer state saved in Jalview Project
+                                                       </li>
+                                                       <li><!-- JAL-2962 -->'Change parameters' option removed from viewer's
+                                                               drop-down menus</li>
+                                                       <li>
+                                                               <!-- JAL-2975 -->Can use shift + arrow keys to rotate PCA image
+                                                               incrementally
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-2965, JAL-1285 -->PCA plot is depth cued
+                                                       </li>
+                                               </ul>
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3127 -->New 'Colour by Sequence ID' option
+                                       </li>
+                                       <li><strong>Speed and Efficiency</strong>
+                                       <ul>
+                                                       <li>
+                                                               <!-- JAL-2185,JAL-3198 -->More efficient creation of selections and
+                                                               multiple groups when working with large alignments
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-3200 -->Speedier import of annotation rows when parsing
+                                                               Stockholm files
+                                                       </li>
+                                               </ul>
+                                       <li><strong>User Interface</strong>
+                                       <ul>
+                                                       <li>
+                                                               <!-- JAL-2933 -->Finder panel remembers last position in each
+                                                               view
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-2527 JAL-3203 -->Alignment Overview now WYSIWIS (What you see is
+                                                               what is shown)<br />Only visible regions of alignment are shown by
+                                                               default (can be changed in user preferences)
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-3169 -->File Chooser stays open after responding Cancel
+                                                               to the Overwrite Dialog
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-2420,JAL-3166 -->Better popup menu behaviour when all
+                                                               sequences are hidden
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-1244 -->Status bar shows bounds when dragging a
+                                                               selection region, and gap count when inserting or deleting gaps
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-3132 -->Status bar updates over sequence and annotation
+                                                               labels
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-3093 -->Annotation tooltips and popup menus are shown
+                                                               when in wrapped mode
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-3073 -->Can select columns by dragging left/right in a graph or histogram
+                                                               annotation
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-2814,JAL-437 -->Help button on Uniprot and PDB search panels
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-2621 -->Cursor changes over draggable box in Overview
+                                                               panel
+                                                       </li>
+                                                       <li>
+                                                               <!-- JAL-3181 -->Consistent ordering of links in sequence id
+                                                               popup menu
+                                                       </li>
+                                                       <li>
+                                                       <!-- JAL-3080 -->Red line indicating tree-cut position not shown if no subgroups are created</li>
+                                                       <li>
+                                                       <!-- JAL-3042 -->Removed ability to configure length of search history by right-clicking search box</li>
+                                                       
+                                                        
+                                               </ul></li>
+                                               <li><!-- JAL-3232 -->Jalview Groovy Scripting Console updated to Groovy v2.5</li> 
+                                       <li><strong>Java 11 Support (not yet on general release)</strong>
+                                               <ul>
+                                                       <li>
+                                                               <!--  -->OSX GUI integrations for App menu's 'About' entry and
+                                                               trapping CMD-Q
+                                                       </li>
+                                               </ul></li>
+                               </ul>
         <em>Deprecations</em>
         <ul>
-          <li>
-            <!-- JAL-3035 -->DAS sequence retrieval and annotation
+          <li><!-- JAL-3035 -->DAS sequence retrieval and annotation
             capabilities removed from the Jalview Desktop
           </li>
-        </ul>
-        <em>Release Processes</em>
-        <ul>
-          <li>Atlassian Bamboo continuous integration server for
-            unattended Test Suite execution</li>
-          <li>
-            <!-- JAL-2864 -->Memory test suite to detect leaks in common
-            operations
-          </li>
-          <li>
-            <!-- JAL-3140 -->IntervalStoreJ (new updatable NCList
-            implementation) used for Sequence Feature collections
-          </li>
-          <li>
-            <!-- JAL-3063 -->Castor library for XML marshalling and
+          <li><!-- JAL-3063,JAL-3116 -->Castor library for XML marshalling and
             unmarshalling has been replaced by JAXB for Jalview projects
-            and XML based data retrieval clients
-          </li>
-          <li>
-          <!-- JAL -->
-
-        </ul>
-      </div></td>
-    <td><div align="left">
-        <em></em>
+            and XML based data retrieval clients</li>
+          <li><!-- JAL-3311 -->Disable VAMSAS menu in preparation for removal</li> 
+          <li><!--  -->Jalview Desktop no longer distributed via Java Web Start</li>
+        </ul> <em>Documentation</em>
+                               <ul>
+                                       <li><!-- JAL-3003 -->Added remarks about transparent rendering effects
+                                               not supported in EPS figure export
+                                       </li>
+                                       <li><!-- JAL-2903 -->Typos in documentation for Preferences dialog</li>
+                               </ul> <em>Development and Release Processes</em>
         <ul>
+               <li>
+               <!-- JAL-3196,JAL-3179.JAL-2671 -->Build system migrated from Ant to Gradle
+                                       </li>
+                       <li>
+                       <!-- JAL-1424 -->Enhanced checks for missing and duplicated keys in Message bundles</li>
           <li>
-            <!-- JAL-3171 -->Warning of 'Duplicate entry' when saving
-            Jalview project involving multiple views
+          <!-- JAL-3225 -->Eclipse project configuration managed with
+            gradle-eclipse
           </li>
           <li>
-            <!-- JAL-3164 -->Overview for complementary view in a linked
-            CDS/Protein alignment is not updated when Hide Columns by
-            Annotation dialog hides columns
+          <!-- JAL-3174,JAL-2886,JAL-2729,JAL-1889 -->Atlassian
+            Bamboo continuous integration for unattended Test Suite
+            execution
           </li>
           <li>
-            <!-- JAL-3158 -->Selection highlighting in the complement of
-            a CDS/Protein alignment stops working after making a
-            selection in one view, then making another selection in the
-            other view.
+          <!-- JAL-2864 -->Memory test suite to detect leaks in common
+            operations
           </li>
-          <li><!-- JAL-3161 -->Annotations tooltip changes beyond visible columns</li>
           <li>
-            <!-- JAL-3154 -->Table Columns could be re-ordered in
-            Feature Settings and Jalview Preferences panels
+          <!-- JAL-2360,JAL-2416 -->More unit test coverage, and minor
+            issues resolved
           </li>
           <li>
-            <!-- JAL-2865 -->Jalview hangs when closing windows
-            or the overview updates with large alignments.
+          <!-- JAL-3248 -->Developer documentation migrated to
+            markdown (with HTML rendering)
           </li>
           <li>
-            <!-- JAL-2865 -->Tree and PCA calculation fails for selected
-            region if columns were selected by dragging right-to-left
-            and the mouse moved to the left of the first column.
+          <!-- JAL-3287 -->HelpLinksChecker runs on Windows
           </li>
           <li>
-            <!-- JAL-2846 -->Error message for trying to load in invalid
-            URLs doesn't tell users the invalid URL
+          <!-- JAL-3289 -->New URLs for publishing development
+            versions of Jalview
           </li>
         </ul>
-        <em>Editing</em>
-        <ul>
+      </td>
+                       <td align="left" valign="top">
+                               <ul>
+                                       <li>
+                                               <!-- JAL-3143 -->Timeouts when retrieving data from Ensembl
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3244 -->'View [Structure] Mappings' and structure
+                                               superposition in Jmol fail on Windows
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3286 -->Blank error dialog is displayed when discovering
+                                               structures for sequences with lots of PDB structures
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3239 -->Text misaligned in EPS or SVG image export with
+                                               monospaced font
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3171 -->Warning of 'Duplicate entry' when saving Jalview
+                                               project involving multiple views
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3164 -->Overview for complementary view in a linked
+                                               CDS/Protein alignment is not updated when Hide Columns by
+                                               Annotation dialog hides columns
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3158 -->Selection highlighting in the complement of a
+                                               CDS/Protein alignment stops working after making a selection in
+                                               one view, then making another selection in the other view
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3161 -->Annotations tooltip changes beyond visible
+                                               columns
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3154 -->Table Columns could be re-ordered in Feature
+                                               Settings and Jalview Preferences panels
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2865 -->Jalview hangs when closing windows, or redrawing the
+                                               overview with large alignments
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2750 -->Tree and PCA calculation fails for selected
+                                               region if columns were selected by dragging right-to-left and the
+                                               mouse moved to the left of the first column
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3218 -->Couldn't hide selected columns adjacent to a
+                                               hidden column marker via scale popup menu
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2846 -->Error message for trying to load in invalid URLs
+                                               doesn't tell users the invalid URL
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2816 -->Tooltips displayed for features filtered by
+                                               score from view
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3330 -->Sequence Variants retrieved from Ensembl during
+                                               show cross references or Fetch Database References are shown in
+                                               red in original view
+                                       </li>
           <li>
-            <!-- JAL-2822 -->Start and End should be updated when
-            sequence data at beginning or end of alignment added/removed
-            via 'Edit' sequence
+            <!-- JAL-2898,JAL-2207 -->stop_gained variants not shown correctly on
+            peptide sequence (computed variant shown as p.Res.null)
           </li>
+                                       <li>
+                                               <!-- JAL-2060 -->'Graduated colour' option not offered for
+                                               manually created features (where feature score is Float.NaN)
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3097,JAL-3099 -->Blank extra columns drawn or printed
+                                               when columns are hidden
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3082 -->Regular expression error for '(' in Select
+                                               Columns by Annotation description
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3072 -->Scroll doesn't stop on mouse up after dragging
+                                               out of Scale or Annotation Panel
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3075 -->Column selection incorrect after scrolling out of
+                                               scale panel
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3074 -->Left/right drag in annotation can scroll
+                                               alignment down
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3108 -->Error if mouse moved before clicking Reveal in
+                                               scale panel
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3002 -->Column display is out by one after Page Down,
+                                               Page Up in wrapped mode
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2839,JAL-781 -->Finder doesn't skip hidden regions
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2932 -->Finder searches in minimised alignments
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2250 -->'Apply Colour to All Groups' not always selected
+                                               on opening an alignment
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3180 -->'Colour by Annotation' not marked selected in
+                                               Colour menu
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3201 -->Per-group Clustal colour scheme changes when
+                                               different groups in the alignment are selected
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2717 -->Internationalised colour scheme names not shown
+                                               correctly in menu
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3206 -->Colour by Annotation can go black at min/max
+                                               threshold limit
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3125 -->Value input for graduated feature colour
+                                               threshold gets 'unrounded'
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2982 -->PCA image export doesn't respect background
+                                               colour
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2963 -->PCA points don't dim when rotated about y axis
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2959 -->PCA Print dialog continues after Cancel
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3078 -->Cancel in Tree Font dialog resets alignment, not
+                                               Tree font
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2964 -->Associate Tree with All Views not restored from
+                                               project file
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2915 -->Scrolling of split frame is sluggish if Overview
+                                               shown in complementary view
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3313 -->Codon consensus incorrectly scaled when shown
+                                               without normalisation
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3021 -->Sequence Details report should open positioned at top
+                                               of report
+                                       </li>
+                                       <li>
+                                               <!-- JAL-914 -->Help page can be opened twice
+                                       </li>
+                                 <li>
+                                 <!-- JAL-3333 -->Fuzzy text in web service status menu on OSX Mojave
+                                 </li>
+                               </ul> <em>Editing</em>
+                               <ul>
+                                       <li>
+                                               <!-- JAL-2822 -->Start and End should be updated when sequence
+                                               data at beginning or end of alignment added/removed via 'Edit'
+                                               sequence
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2541,JAL-2684 (tests) -->Delete/Cut selection doesn't
+                                               relocate sequence features correctly when start of sequence is
+                                               removed (Known defect since 2.10)
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2830 -->Inserting gap sequence via the Edit Sequence
+                                               dialog corrupts dataset sequence
+                                       </li>
+                                       <li>
+                                               <!-- JAL-868 -->Structure colours not updated when associated tree
+                                               repartitions the alignment view (Regression in 2.10.5)
+                                       </li>
+                               </ul> <em>Datamodel</em>
+        <ul>
           <li>
-            <!-- JAL-2541 -->Delete/Cut selection doesn't relocate
-            sequence features correctly when start of sequence is
-            removed (Known defect since 2.10)
+            <!-- JAL-2986 -->Sequence.findIndex returns wrong value when
+            sequence's End is greater than its length
           </li>
+        </ul> <em>Bugs fixed for Java 11 Support (not yet on
+          general release)</em>
+        <ul>
           <li>
-            <!-- JAL- -->
+            <!-- JAL-3288 -->Menus work properly in split-screen
           </li>
-        </ul>
-        <em>New Known Defects</em>
+        </ul> <em>New Known Defects</em>
         <ul>
+                               <li>
+                               <!-- JAL-3340 -->Select columns containing feature by double clicking ignores bounds of an existing selected region
+                               </li>
+                                       <li>
+                                               <!-- JAL-3313 -->Codon consensus logo incorrectly scaled in gapped
+                                               regions of protein alignment.
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2647 -->Input Data menu entry is greyed out when PCA View
+                                               is restored from a Jalview 2.11 project
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3213 -->Alignment panel height can be too small after
+                                               'New View'
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3240 -->Display is incorrect after removing gapped
+                                               columns within hidden columns
+                                       </li>
+                                       <li>
+                                               <!-- JAL-3314 -->Rightmost selection is lost when mouse re-enters
+                                               window after dragging left to select columns to left of visible
+                                               region
+                                       </li>
+                                       <li>
+                                               <!-- JAL-2876 -->Features coloured according to their description
+                                               string and thresholded by score in earlier versions of Jalview are
+                                               not shown as thresholded features in 2.11. To workaround please
+                                               create a Score filter instead.
+                                       </li>
+                                       <li>
+                                       <!-- JAL-3184 -->Cancel on Feature Settings dialog doesn't reset group visibility</li> 
+                                       <li>
+                                       <!-- JAL-3338 -->F2 doesn't enable/disable keyboard mode in linked CDS/Protein view
+                                       </li>
           <li>
-            <!-- JAL-3178 -->Nonpositional features lose feature group
-            on export as jalview features file
+            <!-- JAL-797 -->Closing tree windows with CMD/CTRL-W for
+            alignments with multiple views can close views unexpectedly
           </li>
-          <li><!-- JAL-2647 -->Input Data menu entry is greyed out when PCA View is restored from a Jalview 2.11 project</li> 
-        </ul>
-      </div></td>
-    </tr>
+          </ul>
+          <em>Java 11 Specific defects</em>
+            <ul>
+              <li>
+                <!-- JAL-3235 -->Jalview Properties file is not sorted
+                alphabetically when saved
+              </li>
+          </ul>
+                       </td>
+               </tr>
     <tr>
     <td width="60" nowrap>
       <div align="center">
@@ -220,7 +549,7 @@ li:before {
               <!-- JAL-247 -->Hidden sequence markers and representative
               sequence bolding included when exporting alignment as EPS,
               SVG, PNG or HTML. <em>Display is configured via the
-                Format menu, or for command-line use via a jalview
+                Format menu, or for command-line use via a Jalview
                 properties file.</em>
             </li>
             <li>
@@ -294,7 +623,7 @@ li:before {
             </li>
             <li>
               <!-- JAL-3106 -->.jvp should be used as default extension
-              (not .jar) when saving a jalview project file.
+              (not .jar) when saving a Jalview project file.
             </li>
             <li>
               <!-- JAL-3105 -->Mac Users: closing a window correctly
@@ -375,6 +704,10 @@ li:before {
                 open URL in the browser can be dropped onto Jalview.</em>
             </li>
           </ul>
+          <em>New Known Defects</em>
+          <ul>
+            <li><!-- JAL-3083 -->Cancel option doesn't reset Colour by Annotation</li>
+          </ul>
         </div></td>
     </tr>
     <tr>
@@ -408,6 +741,11 @@ li:before {
               <!-- JAL-2593 -->Improved performance when rendering lots
               of features (particularly when transparency is disabled)
             </li>
+            <li>
+              <!-- JAL-2296,JAL-2295 -->Experimental features in 2.10.2 for
+              exchange of Jalview features and Chimera attributes made
+              generally available
+            </li>
           </ul>
           </div>
       </td>
@@ -529,7 +867,11 @@ li:before {
               Sequences' enabled) or Ensembl isoforms (Workaround in
               2.10.4 is to fail back to N&amp;W mapping)
             </li>
-          </ul>
+                                               <li>
+                                                       <!-- JAL-2990 -->Export Annotations from File Menu with CSV
+                                                       option gives blank output
+                                               </li>
+                                       </ul>
         </div>
           </td>
     </tr>
@@ -1281,6 +1623,15 @@ li:before {
               <!-- JAL-2589 -->User defined gap colour not shown in
               overview when features overlaid on alignment
             </li>
+            <li>
+              <!-- JAL-2567 -->Feature settings for different views not
+              recovered correctly from Jalview project file
+            </li>
+            <li>
+              <!-- JAL-2256 -->Feature colours in overview when first opened
+              (automatically via preferences) are different to the main
+              alignment panel
+            </li>
           </ul>
           <em>Data import/export</em>
           <ul>
diff --git a/help/help/html/whatsNew.html b/help/help/html/whatsNew.html
new file mode 100755 (executable)
index 0000000..4b406be
--- /dev/null
@@ -0,0 +1,104 @@
+<html>
+<!--
+ * 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.
+ -->
+<head>
+<title>What's new ?</title>
+</head>
+<body>
+  <p>
+    <strong>Jalview 2.11 - new installer and new capabilities</strong>
+  </p>
+  <p>Jalview 2.11 introduces support for loading VCF files, and new
+    filters and shading models for sequence features. Under the hood,
+    we've addressed many bugs, and also made some important changes in
+    the way the Jalview desktop is installed and launched.</p>
+  <ul>
+    <li><strong>The Jalview Launcher and Update System</strong>.
+      Jalview's new installation model means you'll only need to
+      download and install Jalview once. After installation, Jalview
+      will automatically keep itself up to date. The launcher also sets
+      Jalview's memory automatically, so you'll never again have to
+      manually configure Java's memory settings.<br />We are grateful
+      to ej Technologies for providing a free open source project
+      license for <a
+      href="https://www.ej-technologies.com/products/install4j/overview.html">install4j</a>,
+      and also to <a
+      href="https://en.wikipedia.org/wiki/Three_Rings_Design">Three
+        Rings Design</a> for Jalview's new over the air update system: <a
+      href="https://github.com/threerings/getdown">Getdown</a>.</li>
+    <li><strong>VCF Support</strong>. Proteins and genomic contigs with
+      chromosomal location annotation (such as protein coding genes
+      retrieved from Ensembl) can be annotated with variants <a
+      href="features/importvcf.html">imported from a local VCF file</a>.</li>
+    <li><strong>Feature filters and attribute colourschemes</strong>. A new
+      <a href="features/featureschemes.html">Feature Display
+        Settings</a> dialog allows filters and feature attribute based
+      colourschemes to be constructed, and a new <em>filters</em> column
+      added to the <a href="features/featuresettings.html">Feature
+        Settings</a> dialog. Jalview's sequence feature datamodel has also
+      been further optimised, and is now maintained as a separate
+      library <em>IntervalStoreJ</em> (available at https://github.com/bartongroup/IntervalStoreJ)</li>
+    <li><strong>Alternative tables for CDS translation</strong>. The <a
+      href="menus/alwcalculate.html">Translate as cDNA</a> option now
+      offers alternative amino acid coding schemes.</li>
+    <li><strong>PCA plots stored in Jalview Projects</strong>. The <a
+      href="calculations/pca.html">PCA viewer</a> user interface has
+      also been improved.</li>
+    <li><strong>Backup files</strong>. Jalview will automatically
+      create backups when overwriting existing files, and - unlike with
+      earlier versions - should Jalview crash during a save, the original
+      file will be unaffected. The <a
+      href="features/preferences.html#backups">Backups tab</a> in
+      Jalview's preferences dialog allows the number and format of
+      backup filenames to be configured.</li>
+  </ul>
+  <p>
+    The full list of bugs fixed in this release can be found in the <a
+      href="releases.html#Jalview.2.11.0">2.11 Release Notes</a>.
+  </p>
+  <p>
+    <strong>Jalview and Java 11, and onwards</strong>
+  </p>
+  <p>The Jalview application comes bundled with its own independent
+    Java installation. Version 2.11.0 includes an AdoptOpenJDK Java 1.8
+    runtime which will be kept up to date. A Java 11 based installation
+    is available from the Jalview development pages.</p>
+  <p>
+    <em>Saying goodbye...</em><br>Long time Jalview users will notice
+    that this release no longer features the
+    <em>Vamsas</em> desktop menu, or a <em>Distributed
+      Annotation System (DAS)</em> tab on the feature settings dialog.
+    DAS is no longer supported by major bioinformatics databases, and we
+    decided that it was no longer feasible to maintain either JDAS or
+    the VAMSAS client library which rely on out-dated Java XML binding
+    technologies. 
+  </p>
+  <p>
+    <em>Next up...</em><br /> Keep an eye on the Jalview web site for
+    news about JalviewJS - the web based JavaScript implementation of
+    Jalview. Whilst Jalview 2.11 has been in development, we have also
+    been working with Prof. Bob Hanson (Jmol and JSmol) to enable
+    Jalview to run as both a Java application and a JavaScript app in a
+    web page. To find out more, open <em>http://www.jalview.org/jalview-js/</em>
+    in Chrome or Firefox.
+  </p>
+</body>
+</html>
similarity index 100%
rename from help/icons/Home.png
rename to help/help/icons/Home.png
similarity index 100%
rename from help/icons/back.png
rename to help/help/icons/back.png
diff --git a/help/html/calculations/pcaviewer.gif b/help/html/calculations/pcaviewer.gif
deleted file mode 100644 (file)
index 78c82f2..0000000
Binary files a/help/html/calculations/pcaviewer.gif and /dev/null differ
diff --git a/help/html/colourSchemes/index.html b/help/html/colourSchemes/index.html
deleted file mode 100755 (executable)
index 51d0738..0000000
+++ /dev/null
@@ -1,403 +0,0 @@
-<html>
-<!--
- * 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.
- -->
-<head>
-<title>Colour Schemes</title>
-<style type="text/css">
-<!--
-td {
-       font-family: "Courier New", Courier, mono;
-       font-style: normal;
-       font-size: medium;
-}
--->
-</style>
-</head>
-<body>
-  <p>
-    <strong>Colour schemes</strong>
-  </p>
-  <p>Jalview allows the user to set a colour scheme for the
-    whole alignment view or for each group defined on regions within it.</p>
-  <p>To change the colour for a view, simply select a new colour scheme from
-    the &quot;Colour&quot; menu.</p>
-  <p>To change the colour of a group, right click on any residue
-    within a group and use the popup menu to define the group colour.</p>
-  <p>At the top of the &quot;Colour&quot; menu you'll see a tick box
-    &quot;Apply Colour to all groups&quot;. This is ticked by
-    default so that a chosen colour scheme will be applied to all
-    existing groups. If you wish to maintain the colour scheme for
-    defined groups, make sure you deselect this option before selecting
-    a new scheme in the Colour menu.</p>
-  <p>
-    The <strong>&quot;Colour&#8594;<a
-      href="../colourSchemes/textcolour.html">Colour Text...</a>&quot;
-    </strong> entry opens a dialog box to set a different text colour for light
-    and dark background, and the intensity threshold for transition
-    between them.
-  </p>
-  <p>The default colour schemes are summarised in the table below:
-  <div align="center">
-    <p>&nbsp;</p>
-    <p>
-      <strong>Protein Colour Schemes</strong>
-    </p>
-    <table border="1">
-      <tr>
-        <td>
-          <table border="1">
-            <tr>
-              <td nowrap></td>
-              <td>A</td>
-              <td>R</td>
-              <td>N</td>
-              <td>D</td>
-              <td>C</td>
-              <td>Q</td>
-              <td>E</td>
-              <td>G</td>
-              <td>H</td>
-              <td>I</td>
-              <td>L</td>
-              <td>K</td>
-              <td>M</td>
-              <td>F</td>
-              <td>P</td>
-              <td>S</td>
-              <td>T</td>
-              <td>W</td>
-              <td>Y</td>
-              <td>V</td>
-              <td>B</td>
-              <td>X</td>
-              <td>Z</td>
-            </tr>
-            <tr>
-              <td height="24">Clustal</td>
-              <td bgcolor="#80a0f0"></td>
-              <td bgcolor="#f01505"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#c048c0"></td>
-              <td bgcolor="#f08080"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#c048c0"></td>
-              <td bgcolor="#f09048"></td>
-              <td bgcolor="#15a4a4"></td>
-              <td bgcolor="#80a0f0"></td>
-              <td bgcolor="#80a0f0"></td>
-              <td bgcolor="#f01505"></td>
-              <td bgcolor="#80a0f0"></td>
-              <td bgcolor="#80a0f0"></td>
-              <td bgcolor="#ffff00"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#80a0f0"></td>
-              <td bgcolor="#15a4a4"></td>
-              <td bgcolor="#80a0f0"></td>
-              <td></td>
-              <td></td>
-              <td></td>
-            </tr>
-            <tr>
-              <td height="24">Zappo</td>
-              <td bgcolor="#ffafaf"></td>
-              <td bgcolor="#6464ff"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#ff0000"></td>
-              <td bgcolor="#ffff00"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#ff0000"></td>
-              <td bgcolor="#ff00ff"></td>
-              <td bgcolor="#6464ff"></td>
-              <td bgcolor="#ffafaf"></td>
-              <td bgcolor="#ffafaf"></td>
-              <td bgcolor="#6464ff"></td>
-              <td bgcolor="#ffafaf"></td>
-              <td bgcolor="#ffc800"></td>
-              <td bgcolor="#ff00ff"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#ffc800"></td>
-              <td bgcolor="#ffc800"></td>
-              <td bgcolor="#ffafaf"></td>
-              <td></td>
-              <td></td>
-              <td></td>
-            </tr>
-            <tr>
-              <td>Taylor</td>
-              <td bgcolor="#ccff00"></td>
-              <td bgcolor="#0000ff"></td>
-              <td bgcolor="#cc00ff"></td>
-              <td bgcolor="#ff0000"></td>
-              <td bgcolor="#ffff00"></td>
-              <td bgcolor="#ff00cc"></td>
-              <td bgcolor="#ff0066"></td>
-              <td bgcolor="#ff9900"></td>
-              <td bgcolor="#0066ff"></td>
-              <td bgcolor="#66ff00"></td>
-              <td bgcolor="#33ff00"></td>
-              <td bgcolor="#6600ff"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#00ff66"></td>
-              <td bgcolor="#ffcc00"></td>
-              <td bgcolor="#ff3300"></td>
-              <td bgcolor="#ff6600"></td>
-              <td bgcolor="#00ccff"></td>
-              <td bgcolor="#00ffcc"></td>
-              <td bgcolor="#99ff00"></td>
-              <td></td>
-              <td></td>
-              <td></td>
-            </tr>
-            <tr>
-              <td>Hydrophobicity</td>
-              <td bgcolor="#ad0052"></td>
-              <td bgcolor="#0000ff"></td>
-              <td bgcolor="#0c00f3"></td>
-              <td bgcolor="#0c00f3"></td>
-              <td bgcolor="#c2003d"></td>
-              <td bgcolor="#0c00f3"></td>
-              <td bgcolor="#0c00f3"></td>
-              <td bgcolor="#6a0095"></td>
-              <td bgcolor="#1500ea"></td>
-              <td bgcolor="#ff0000"></td>
-              <td bgcolor="#ea0015"></td>
-              <td bgcolor="#0000ff"></td>
-              <td bgcolor="#b0004f"></td>
-              <td bgcolor="#cb0034"></td>
-              <td bgcolor="#4600b9"></td>
-              <td bgcolor="#5e00a1"></td>
-              <td bgcolor="#61009e"></td>
-              <td bgcolor="#5b00a4"></td>
-              <td bgcolor="#4f00b0"></td>
-              <td bgcolor="#f60009"></td>
-              <td bgcolor="#0c00f3"></td>
-              <td bgcolor="#680097"></td>
-              <td bgcolor="#0c00f3"></td>
-            </tr>
-            <tr>
-              <td>Helix Propensity</td>
-              <td bgcolor="#e718e7"></td>
-              <td bgcolor="#6f906f"></td>
-              <td bgcolor="#1be41b"></td>
-              <td bgcolor="#778877"></td>
-              <td bgcolor="#23dc23"></td>
-              <td bgcolor="#926d92"></td>
-              <td bgcolor="#ff00ff"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#758a75"></td>
-              <td bgcolor="#8a758a"></td>
-              <td bgcolor="#ae51ae"></td>
-              <td bgcolor="#a05fa0"></td>
-              <td bgcolor="#ef10ef"></td>
-              <td bgcolor="#986798"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#36c936"></td>
-              <td bgcolor="#47b847"></td>
-              <td bgcolor="#8a758a"></td>
-              <td bgcolor="#21de21"></td>
-              <td bgcolor="#857a85"></td>
-              <td bgcolor="#49b649"></td>
-              <td bgcolor="#758a75"></td>
-              <td bgcolor="#c936c9"></td>
-            </tr>
-            <tr>
-              <td nowrap>Strand Propensity</td>
-              <td bgcolor="#5858a7"></td>
-              <td bgcolor="#6b6b94"></td>
-              <td bgcolor="#64649b"></td>
-              <td bgcolor="#2121de"></td>
-              <td bgcolor="#9d9d62"></td>
-              <td bgcolor="#8c8c73"></td>
-              <td bgcolor="#0000ff"></td>
-              <td bgcolor="#4949b6"></td>
-              <td bgcolor="#60609f"></td>
-              <td bgcolor="#ecec13"></td>
-              <td bgcolor="#b2b24d"></td>
-              <td bgcolor="#4747b8"></td>
-              <td bgcolor="#82827d"></td>
-              <td bgcolor="#c2c23d"></td>
-              <td bgcolor="#2323dc"></td>
-              <td bgcolor="#4949b6"></td>
-              <td bgcolor="#9d9d62"></td>
-              <td bgcolor="#c0c03f"></td>
-              <td bgcolor="#d3d32c"></td>
-              <td bgcolor="#ffff00"></td>
-              <td bgcolor="#4343bc"></td>
-              <td bgcolor="#797986"></td>
-              <td bgcolor="#4747b8"></td>
-            </tr>
-            <tr>
-              <td>Turn Propensity</td>
-              <td bgcolor="#2cd3d3"></td>
-              <td bgcolor="#708f8f"></td>
-              <td bgcolor="#ff0000"></td>
-              <td bgcolor="#e81717"></td>
-              <td bgcolor="#a85757"></td>
-              <td bgcolor="#3fc0c0"></td>
-              <td bgcolor="#778888"></td>
-              <td bgcolor="#ff0000"></td>
-              <td bgcolor="#708f8f"></td>
-              <td bgcolor="#00ffff"></td>
-              <td bgcolor="#1ce3e3"></td>
-              <td bgcolor="#7e8181"></td>
-              <td bgcolor="#1ee1e1"></td>
-              <td bgcolor="#1ee1e1"></td>
-              <td bgcolor="#f60909"></td>
-              <td bgcolor="#e11e1e"></td>
-              <td bgcolor="#738c8c"></td>
-              <td bgcolor="#738c8c"></td>
-              <td bgcolor="#9d6262"></td>
-              <td bgcolor="#07f8f8"></td>
-              <td bgcolor="#f30c0c"></td>
-              <td bgcolor="#7c8383"></td>
-              <td bgcolor="#5ba4a4"></td>
-            </tr>
-            <tr>
-              <td>Buried Index</td>
-              <td bgcolor="#00a35c"></td>
-              <td bgcolor="#00fc03"></td>
-              <td bgcolor="#00eb14"></td>
-              <td bgcolor="#00eb14"></td>
-              <td bgcolor="#0000ff"></td>
-              <td bgcolor="#00f10e"></td>
-              <td bgcolor="#00f10e"></td>
-              <td bgcolor="#009d62"></td>
-              <td bgcolor="#00d52a"></td>
-              <td bgcolor="#0054ab"></td>
-              <td bgcolor="#007b84"></td>
-              <td bgcolor="#00ff00"></td>
-              <td bgcolor="#009768"></td>
-              <td bgcolor="#008778"></td>
-              <td bgcolor="#00e01f"></td>
-              <td bgcolor="#00d52a"></td>
-              <td bgcolor="#00db24"></td>
-              <td bgcolor="#00a857"></td>
-              <td bgcolor="#00e619"></td>
-              <td bgcolor="#005fa0"></td>
-              <td bgcolor="#00eb14"></td>
-              <td bgcolor="#00b649"></td>
-              <td bgcolor="#00f10e"></td>
-            </tr>
-          </table>
-        </td>
-      </tr>
-    </table>
-    <p>&nbsp;</p>
-    <p>
-      <strong>Nucleotide Colour Schemes</strong>
-    </p>
-    <table border="1">
-      <tr>
-        <td>
-          <table border="1">
-            <tr>
-              <td nowrap></td>
-              <td>A</td>
-              <!--Adenine-->
-              <td>C</td>
-              <!--Cytosine-->
-              <td>G</td>
-              <!--Guanine-->
-              <td>T</td>
-              <!--Thymine-->
-              <td>U</td>
-              <!--Uracil-->
-              <td>I</td>
-              <!--Inosine-->
-              <td>X</td>
-              <!--Xanthine-->
-              <td>R</td>
-              <!--Unknown Purine-->
-              <td>Y</td>
-              <!--Unknown Pyrimidine-->
-              <td>N</td>
-              <!--Unknown-->
-              <td>W</td>
-              <!--Weak nucleotide (A or T)-->
-              <td>S</td>
-              <!--Strong nucleotide (G or C)-->
-              <td>M</td>
-              <!--Amino (A or C)-->
-              <td>K</td>
-              <!--Keto (G or T)-->
-              <td>B</td>
-              <!--Not A (G or C or T)-->
-              <td>H</td>
-              <!--Not G (A or C or T)-->
-              <td>D</td>
-              <!--Not C (A or G or T)-->
-              <td>V</td>
-              <!--Not T (A or G or C-->
-            </tr>
-            <tr>
-              <td height="24">Nucleotide</td>
-              <td bgcolor="#64F73F"></td>
-              <td bgcolor="#FFB340"></td>
-              <td bgcolor="#EB413C"></td>
-              <td bgcolor="#3C88EE"></td>
-              <td bgcolor="#3C88EE"></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-            </tr>
-            <tr>
-              <td height="24">Purine/Pyrimidine</td>
-              <td bgcolor="#FF83FA"></td>
-              <td bgcolor="#40E0D0"></td>
-              <td bgcolor="#FF83FA"></td>
-              <td bgcolor="#40E0D0"></td>
-              <td bgcolor="#40E0D0"></td>
-              <td></td>
-              <td></td>
-              <td bgcolor="#FF83FA"></td>
-              <td bgcolor="#40E0D0"></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-              <td></td>
-            </tr>
-          </table>
-        </td>
-      </tr>
-    </table>
-
-
-  </div>
-  <p align="center">&nbsp;</p>
-</body>
-</html>
diff --git a/help/html/features/commandline.html b/help/html/features/commandline.html
deleted file mode 100644 (file)
index 92d9323..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-<html>
-<!--
- * 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.
- -->
-<head>
-<title>Running Jalview from the command line</title>
-</head>
-<body>
-  <p>
-    <strong>Running Jalview from the command line</strong>
-  </p>
-  <p>Jalview is most easily run from the command line if you have
-    built it from source, or via the 'Jalview' executable created from
-    the InstallAnywhere Jalview installation. Both of these mechanisms
-    allow true command line execution of Jalview - allowing you to
-    provide additional options.</p>
-  <p>The Java Webstart version of Jalview can be executed from the
-    command line using something like :
-  <pre>javaws http://www.jalview.org/webstart/jalview.jnlp -open
- <em>yourFileName</em>
-  </pre>
-  But, this is not guaranteed to work on all versions of webstart on all
-  operating systems, and doesn't let you execute Jalview with any
-  additional parameters.
-  <p>
-    <strong>Running Jalview from the InstallAnywhere
-      installation</strong>
-  </p>
-  <p>
-    If you install with InstallAnywhere you can use several more
-    commands. However, if you call the application with the link
-    provided by InstallAnywhere any output from the application will be
-    sent to output.txt, not standard out.<br> The Jalview
-    application also requires a number of additional libraries on the
-    class path. The command line below adds all the jar files in the
-    Jalview installation's 'lib' directory to the classpath, as well as
-    the Jalview application jar file:
-  </p>
-  <pre>java -classpath "$INSTALL_DIR$/lib/*:$INSTALL_DIR$/jalview.jar" jalview.bin.Jalview -open [FILE] </pre>
-  <p>
-    Use '-help' to get more information on the <a
-      href="clarguments.html">command line arguments</a> that
-    Jalview accepts.
-  </p>
-  <p>&nbsp;</p>
-  <p>&nbsp;</p>
-</body>
-</html>
diff --git a/help/html/features/featureSettings.gif b/help/html/features/featureSettings.gif
deleted file mode 100755 (executable)
index 31f245c..0000000
Binary files a/help/html/features/featureSettings.gif and /dev/null differ
diff --git a/help/html/features/featurecoloursettings.gif b/help/html/features/featurecoloursettings.gif
deleted file mode 100644 (file)
index 1b889db..0000000
Binary files a/help/html/features/featurecoloursettings.gif and /dev/null differ
diff --git a/help/html/features/featureschemes.html b/help/html/features/featureschemes.html
deleted file mode 100644 (file)
index 37243d2..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-<html>
-<!--
- * 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.
- -->
-<head>
-<title>Sequence feature colour schemes</title>
-</head>
-
-<body>
-  <p>
-    <strong>Sequence feature colour schemes</strong>
-  </p>
-  <p>
-    Jalview can visualize annotation present on parts of a sequence by
-    overlaying colours according to the annotation present at each
-    position. It allows features to be colored either according to type,
-    or for a particular type, according to an individual feature's
-    associated label or score. The <a href="featuresettings.html">Feature
-      Settings dialog box</a> controls the order and display of each
-    sequence annotation type, and also allows the colour scheme used for
-    the the feature to be changed <em>via</em> the 'Graduated Colour'
-    option in the sequence feature pop-up menu (obtained by
-    right-clicking on a sequence feature).
-  </p>
-
-  <center>
-    <img src="featurecoloursettings.gif"><br />Graduated feature
-    colour scheme settings dialog box</img>
-  </center>
-  <p>The Graduated Feature Colour dialog box has the following
-    controls:
-  <ul>
-    <li><em>Colour by Label</em> - when checked this derives a
-      colour for each feature based on the label text.</li>
-    <li><em>Min and Max Color boxes</em> - Click on these boxes to
-      set the minimum and maximum colours used to shade features by
-      their associated score.</li>
-    <li><em>Threshold type combo box</em> - Allows features to be
-      hidden features if their score is below or above a given
-      threshold.<br /> <em>Set the threshold using the slider or
-        type it in to the text box. The threshold cannot be set outside
-        the available range of feature scores.</em></li>
-    <li><em>Threshold is Min/Max</em> - When checked, the threshold
-      will be used as the upper or lower limit when shading the features
-      according to their score using the Min and Max colour.</li>
-  </ul>
-  </p>
-  <p>
-    <strong>Icon styles for graduated feature styles</strong><br />
-  <p>
-    When a graduated colourscheme is applied to a feature, it is
-    indicated in the feature settings or amend feature dialog box by the
-    following types of icon:<br />
-  <center>
-    <table width="50%" border="1">
-      <tr>
-        <td><strong>Type of Colouring</strong></td>
-        <td><strong>Icon</strong></td>
-      </tr>
-      <tr>
-        <td><em>Graduated colour by Feature Score</em></td>
-        <td><img src="fcsminmax.gif" /></td>
-      </tr>
-      <tr>
-        <td><em>Graduated colour thresholded (less than) by
-            feature Score</em></td>
-        <td><img src="fcsltthan.gif" /></td>
-      </tr>
-      <tr>
-        <td><em>Graduated colour thresholded (greater than) by
-            feature Score</em></td>
-        <td><img src="fcsgtthan.gif" /></td>
-      </tr>
-      <tr>
-        <td><em>Colour by Feature Label (may also be
-            thresholded)</em></td>
-        <td><img src="fcsntlabel.gif" /></td>
-      </tr>
-      <tr>
-        <td align="center" colspan="2"><em> <strong>The
-              current threshold is given in the icon's tooltip.</strong>
-        </em></td>
-      </tr>
-    </table>
-  </center>
-  </p>
-  <em>Graduated feature colours were introduced in Jalview 2.5</em>
-</body>
-</html>
diff --git a/help/html/features/jalarchive.html b/help/html/features/jalarchive.html
deleted file mode 100755 (executable)
index 94180a6..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-<html>
-<!--
- * 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.
- -->
-<head>
-<title>Jalview Archives</title>
-</head>
-<body>
-
-  <p>
-    <strong>Jalview Project Archives</strong>
-  </p>
-
-  <p>These are java archives of an XML file containing alignments,
-    trees and Jalview display information. A data exchange standard is
-    currently being developed, so there is no stable schema yet, but
-    Jalview web services will soon make use of the same interchange
-    format.</p>
-  <p>
-    For those who want to know...<br> Jalview uses java classes
-    automatically created using <a href="http://www.castor.org">Castor</a>.
-    Jalview 2.08 uses descriptor classes which significantly increase
-    the speed of marshalling / unmarshalling java objects into XML.
-    Files created prior to Jalview 2.08 can still be read in, but they
-    will be saved in the new format.
-  </p>
-</body>
-</html>
diff --git a/help/html/memory.html b/help/html/memory.html
deleted file mode 100755 (executable)
index 9437a60..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-<html>
-<!--
- * 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.
- -->
-<head>
-<title>Memory Settings</title>
-</head>
-<body>
-  <h2>
-    <center>
-      <strong>Memory Usage Settings for Jalview</strong>
-    </center>
-  </h2>
-  <p>
-    Jalview sometimes runs out of memory. This is because of the way
-    that Java runs on a computer - what is actually run is a program
-    called a virtual machine (the JVM) which executes the java
-    instructions. The JVM has limits on the memory that can be allocated
-    to the java program - and you might need to increase them if you are
-    working with particularly large datasets.<br> If Jalview has
-    not explicitly told you that it has run out of memory, then a common
-    sign is that a function that normally works seems to have no effect
-    when working with a larger set of sequences (this might include open
-    dialog boxes for saving PNG files, or when interpreting the result
-    of a web service calculation).
-  </p>
-  <p>
-    <em>Jalview Memory Usage Monitor</em>: If you are concerned about
-    memory, or think that things might be behaving strangely because of
-    a shortage of memory, then you can check this by enabling the memory
-    usage monitor. This is done by selecting the <strong>Tools&#8594;Show
-      Memory Usage</strong> option. Once enabled, the memory usage monitor
-    displays the currently available memory, the total memory, and the
-    percentage free at the bottom left hand side of the Jalview Desktop
-    window's background.
-  </p>
-  <p>
-    <em>Increasing the memory available to Jalview</em><br> The way
-    you increase the memory settings for the JVM depends on which
-    installation of Jalview you use:
-  </p>
-  <ul>
-    <li><em><font size="3">Web Start Version</font></em>
-      <p>
-        JavaWS sets the JVM parameters through special tags in the JNLP
-        file. You can obtain a JNLP file with modified memory settings
-        from our service with the following link (replace 2G with
-        desired memory in G or M):<br /> <a
-          href="http://www.jalview.org/services/launchApp?jvm-max-heap=2G">http://www.jalview.org/services/launchApp?jvm-max-heap=2G</a>
-      </p>
-      <p>
-        Alternatively, if you want to create your own JNLP file then
-        please download the latest JNLP file from <a
-          href="http://www.jalview.org/webstart/jalview.jnlp">http://www.jalview.org/webstart/jalview.jnlp</a>
-        and modify the max-heap-size parameter for the j2se tag in the
-        &lt;resources&gt; element. e.g.
-      <pre>
-&lt;j2se version="1.7+" initial-heap-size="500M" max-heap-size="1000M"/&gt;
-</pre> In both cases, you should save your new jnlp file somewhere and then
-      either point your web browser at the file's url, launch it from
-      your file browser, or from a terminal window run javaws (located
-      in your Java installation's bin directory) with the file location
-      as its argument. The file's url is something like :<br> <pre>
-file://&lt;full path to file&gt;
-</pre> If Jalview doesn't start up, see <a href="#memsetting">below</a>.
-      You'll have to edit the above settings in the JNLP file using a
-      text editor, save it, and try starting Jalview with it once more.
-      </p></li>
-    <li><em><font size="3">Install Anywhere version</font></em>
-      <p>You need to change the InstallAnywhere configuration
-        settings for the application. These are found in different
-        places depending upon which operating system you have :
-      <ul>
-        <li><em>Unix/Windows</em>
-          <p>Take a look inside the Jalview program installation
-            directory (this might be in C:\Program Files\Jalview on
-            windows). You should find a file called 'Jalview.lax' in it
-            - make a backup, and then add the following lines to the end
-            of the original file :
-          <pre>
-lax.nl.java.option.java.heap.size.max=1000m
-lax.nl.java.option.java.heap.size.initial=500m
-</pre> Case and (lack of) spaces are important here! Do not add any spaces
-          after the m in each line, and do not put any spaces before
-          'lax'.<br> Also there MUST be a carriage return after the
-          final line.
-          <p></p></li>
-        <li><em>Mac OSX</em>
-          <p>
-            The lines you need to change are in the <em>Info.plist</em>
-            file inside the <em>Jalview.app/Contents</em> directory
-            (which is where the installAnywhere installation was made) :
-
-          
-          <pre>
-&lt;key&ht;VMOptions&lt;/key&ht;
-&lt;array&gt;
-! &lt;string&gt;-Xms2M&lt;/string&gt;
-! &lt;string&gt;-Xmx64M&lt;/string&gt;
-&lt;/array&gt;
-</pre>Exchange the above two string tags for : <pre>
-&lt;string&gt;-Xms500M&lt;/string&gt;
-&lt;string&gt;-Xmx1000M&lt;/string&gt;
-</pre>
-          <p></p></li>
-      </ul>
-    <li><font size="3"><em>In all cases</em></font><br> Save
-      the file and try to start Jalview in the normal way. If it doesn't
-      start, see below...</li>
-  </ul>
-  <p>
-    <em>Please Note:</em> We do modify the default memory settings in
-    Jalview from time to time, so you may find different numbers to
-    those shown in the examples above.
-  </p>
-  <font size="3"><em>Jalview doesn't start... What do the
-      memory settings mean ?<a name="memsetting"></a>
-  </em></font>
-  <p>The 1000m value corresponds to the maximum number of megabytes
-    of space that java objects can occupy. The 500m is the initial heap
-    size that java will run in - increasing this can speed up memory
-    allocation if you know you will need 500 meg of memory to begin with
-    (ie it should speed up loading large alignments).</p>
-  <p>If, after setting the initial and maximum heap size to some
-    large value, you cannot actually start Jalview, then the max and
-    initial sizes are too big for your machine (there seems to be a
-    physical limit related to physical RAM - email the usual address to
-    enlighten us if you know better!). Our experiments found 1000m to be
-    the biggest setting that could be used on a 1GB machine. Just try
-    reducing the sizes until Jalview starts up properly!</p>
-  <p>
-    We increased the default memory in Jalview 2.10.5 to 1G. To launch
-    Jalview with the pre 2.10.5 default memory allocation, use the <a
-      href="http://www.jalview.org/webstart/jalview_256MB.jnlp">Jalview
-      256MB JNLP</a>.
-  </p>
-  <p>&nbsp;</p>
-</body>
-</html>
diff --git a/help/html/vamsas/index.html b/help/html/vamsas/index.html
deleted file mode 100644 (file)
index 72a336a..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-<html>
-<!--
- * 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.
- -->
-<head>
-<title>VAMSAS Interoperation</title>
-</head>
-<body>
-  <p>
-    <strong>VAMSAS Interoperation</strong>
-  </p>
-  <p>
-    Jalview can interact with other applications using &quot;the <strong>VAMSAS
-      Interoperation framework</strong>&quot; which is an experimental model for
-    interoperation between bioinformatics applications (<strong>V</strong>isualization
-    and <strong>A</strong>nalysis of <strong>Molecular</strong> <strong>S</strong>equences,
-    <strong>Alignements</strong> and <strong>S</strong>tructures).
-    Currently, the only other VAMSAS enabled application is <a
-      href="http://www.topali.org">TOPALi</a> - a user friendly
-    program for phylogenetics and evolutionary analysis.
-  <p>
-    VAMSAS enabled applications access a shared bioinformatics dataset
-    containing sequences, alignments, annotation and trees, which can be
-    represented by an XML document analogous to a <a
-      href="../features/jalarchive.html">Jalview Project
-      Archive</a>.
-  </p>
-  <br>
-  <strong>Connecting to a VAMSAS session</strong>
-  <br> The VAMSAS functionality in Jalview is accessed through the
-  Desktop's
-  <strong>Vamsas</strong> menu. The options available in this menu
-  depend on whether the application is currently interacting with a
-  VAMSAS dataset in a
-  <strong>VAMSAS session</strong>. When the application is not connected
-  to a session is active, the menu options are as follows:
-  <br>
-  <ul>
-    <li><em>Connect to an existing session</em><br> If
-      visible, this submenu contains a list of existing sessions that
-      the VAMSAS framework has discovered on your computer. <br>
-      Choose one to connect to it.</li>
-    <li><em>New VAMSAS Session</em><br> This option will
-      create a new session on your computer.</li>
-    <li><em>Load VAMSAS Session...</em><br> This option will
-      open a file browser window allowing you to select a VAMSAS session
-      archive from which a new session will be created.<br /> <em>New
-        in 2.5:</em>Sessions created from an imported document inherit the
-      file or URL for the document.</li>
-
-  </ul>
-  <br>
-  <strong>VAMSAS and Firewalls</strong>: VAMSAS uses sockets to
-  communicate between different programs. This means that after starting
-  a session, your firewall software may ask you whether to allow the
-  java executable access to the internet (port 53782). If you do not
-  allow this, messages will not be exchanged with other VAMSAS
-  applications.
-  </br>
-  <br> Once you have successfully connected to a VAMSAS session,
-  any data made available by other VAMSAS applications will be
-  automatically imported into Jalview. However, in order to share the
-  data in Jalview with other VAMSAS applications, you must manually
-  select the
-  <strong>Vamsas&#8594;&quot;Session Update&quot;</strong> entry that is
-  visible when a session is active. Selecting this option will update
-  the VAMSAS session document, with the data loaded into Jalview. Any
-  new alignments, trees and annotation will be written to the session,
-  in addition to any edits you have made to data originally stored in
-  the document.
-  <br>
-  <strong>Saving the current session</strong>
-  <br> You can save the current session as a VAMSAS Session archive
-  using the
-  <strong>Vamsas&#8594;&quot;Session Update&quot;</strong>. The file
-  contains a snapshot of the current VAMSAS session, including data from
-  any other applications connected to the session.
-  <strong>Leaving a VAMSAS session</strong>
-  <br> A session can be disconnected from at any time using the
-  <strong>Vamsas&#8594;&quot;Stop Session&quot;</strong> option.
-  Selecting this option will only disconnect Jalview from the session -
-  any other applications will remain connected to the session. If
-  Jalview is the only application connected to the session and you have
-  not yet saved the VAMSAS session then you will be prompted with an
-  optional 'Save VAMSAS session...' dialog box, allowing the session to
-  be saved and returned to at a later date.
-  <br>
-  <strong>VAMSAS Session Persistence</strong>
-  <br> VAMSAS sessions are persistent - this means that they exist
-  independently of any VAMSAS applications that are connected to them.
-  This means that if something goes wrong with a VAMSAS application and
-  it crashes or otherwise fails, the VAMSAS session it is connected to
-  will (hopefully) be unaffected. For instance, if Jalview is killed or
-  crashes whilst it is still connected to a session, that session can be
-  recovered in a new Jalview instance using the
-  <strong>Vamsas&#8594;&quot;Existing session&quot;</strong> sub menu.
-  </p>
-  <p>
-    <strong>A quick Demo</strong> <br> Jalview can talk to itself
-    through VAMSAS. Simply start two copies of the application, create a
-    new vamsas session in one, and connect to the new session in the
-    other. Then load your data into one of the applications, and use the
-    <strong>Vamsas&#8594;&quot;Session Update&quot;</strong> menu entry
-    to try to propagate the data to the other application. <br>
-  <table>
-    <tr>
-      <td>Data Sharing Capability</td>
-      <td>Jalview Version</td>
-    </tr>
-    <tr>
-      <td>Alignments, sequences and annotation, trees, database
-        references, cDNA/protein mappings.</td>
-      <td>2.4</td>
-    </tr>
-    <tr>
-      <td>Mouseover location across linked DNA, protein and
-        structure positions.</td>
-      <td>2.4</td>
-    </tr>
-    <tr>
-      <td>Jalview project settings (Multiple views, groups, tree
-        partitions, colouring, window positions)</td>
-      <td>2.5</td>
-    </tr>
-    <tr>
-      <td>Sequence region and column selections</td>
-      <td>2.5</td>
-    </tr>
-  </table>
-  <br />
-  <p>
-    Version 0.2 of the VAMSAS client library is used in <em>Jalview
-      2.5</em>. For further details about the VAMSAS framework, please check
-    the <a href="http://www.vamsas.ac.uk">VAMSAS website</a>. The VAMSAS
-    framework is implemented as a Java 1.4 Library and depends on a
-    number of other open source projects. Its source is released under
-    the LGPL license. &nbsp;
-  </p>
-</body>
-</html>
diff --git a/help/html/whatsNew.html b/help/html/whatsNew.html
deleted file mode 100755 (executable)
index 3538ff4..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-<html>
-<!--
- * 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.
- -->
-<head>
-<title>What's new ?</title>
-</head>
-<body>
-  <p>
-    <strong>Jalview 2.11 - major and minor new features</strong>
-  </p>
-  <p>Jalview 2.11 introduces support for loading VCF files, and new
-    filters and shading models for sequence features. Under the hood,
-    we've addressed many bugs, and also made some important changes in
-    the way the Jalview desktop is installed and launched.</p>
-  <ul>
-    <li><em>VCF Support</em>. Proteins and genomic contigs with
-      chromosomal location annotation (such as protein coding genes
-      retrieved from Ensembl) can be annotated with variants imported
-      from a local VCF file.</li>
-    <li><em>The Jalview Launcher and Update System</em><br />
-      Jalview's new installation model means you'll only need to
-      download and install Jalview once. After installation, Jalview
-      will automatically keep itself up to date. The launcher also sets
-      Jalview's memory automatically, so you'll never again have to
-      manually configure Java's memory settings.<br />We are grateful to
-      Install4J who provided us with a free license for their
-      installation system, and Jalview's over the air update system is
-      via Getdown.</li>
-  </ul>
-  <p>
-    The full list of bugs fixed in this release can be found in the <a
-      href="releases.html#Jalview.2.11">2.11 Release Notes</a>.
-  </p>
-  <p>
-    <strong>Jalview and Java 11, 13, and onwards</strong>
-  </p>
-  <p>Java 11 provides improved performance and better OS
-    integration, so we now recommend users select our Java 11 Jalview
-    distribution rather than the legacy Java 8 build.</p>
-  <em>Known Issues - update for 211 </em>
-  <ul>
-    <li>OSX: The 'Open File' dialog for Jalview's Groovy Console
-      appears with the title 'Save As', and attempting to select a file
-      to load yields a FileNotFound exception.</br>The workaround is to first
-      clear the 'Untitled' filename before selecting the file you wish
-      to load.
-    </li>
-    <li>OSX: Links don't open when clicked on or via the Sequence
-      or Alignment window popup menu.</li>
-    <li>OSX (Webstart): Jalview only displays old news feed items</li>
-  </ul>
-</body>
-</html>
diff --git a/j11lib/FastInfoset.jar b/j11lib/FastInfoset.jar
new file mode 100644 (file)
index 0000000..2671209
Binary files /dev/null and b/j11lib/FastInfoset.jar differ
similarity index 100%
rename from lib/VARNAv3-93.jar
rename to j11lib/VARNAv3-93.jar
similarity index 100%
rename from lib/VAqua5-patch.jar
rename to j11lib/VAqua5-patch.jar
similarity index 100%
rename from lib/axis.jar
rename to j11lib/axis.jar
diff --git a/j11lib/commons-compress-1.18.jar b/j11lib/commons-compress-1.18.jar
new file mode 100644 (file)
index 0000000..e401046
Binary files /dev/null and b/j11lib/commons-compress-1.18.jar differ
diff --git a/j11lib/getdown-core.jar b/j11lib/getdown-core.jar
new file mode 100644 (file)
index 0000000..bbe1476
Binary files /dev/null and b/j11lib/getdown-core.jar differ
diff --git a/j11lib/gmbal-api-only-MODULE.jar b/j11lib/gmbal-api-only-MODULE.jar
new file mode 100644 (file)
index 0000000..06a5fcc
Binary files /dev/null and b/j11lib/gmbal-api-only-MODULE.jar differ
diff --git a/j11lib/groovy-2.5.7.jar b/j11lib/groovy-2.5.7.jar
new file mode 100644 (file)
index 0000000..e391e93
Binary files /dev/null and b/j11lib/groovy-2.5.7.jar differ
diff --git a/j11lib/groovy-ant-2.5.7.jar b/j11lib/groovy-ant-2.5.7.jar
new file mode 100644 (file)
index 0000000..155ec4c
Binary files /dev/null and b/j11lib/groovy-ant-2.5.7.jar differ
diff --git a/j11lib/groovy-bsf-2.5.7.jar b/j11lib/groovy-bsf-2.5.7.jar
new file mode 100644 (file)
index 0000000..3074e3f
Binary files /dev/null and b/j11lib/groovy-bsf-2.5.7.jar differ
diff --git a/j11lib/groovy-cli-commons-2.5.7.jar b/j11lib/groovy-cli-commons-2.5.7.jar
new file mode 100644 (file)
index 0000000..cf54453
Binary files /dev/null and b/j11lib/groovy-cli-commons-2.5.7.jar differ
diff --git a/j11lib/groovy-cli-picocli-2.5.7.jar b/j11lib/groovy-cli-picocli-2.5.7.jar
new file mode 100644 (file)
index 0000000..a05c3e4
Binary files /dev/null and b/j11lib/groovy-cli-picocli-2.5.7.jar differ
diff --git a/j11lib/groovy-console-2.5.7.jar b/j11lib/groovy-console-2.5.7.jar
new file mode 100644 (file)
index 0000000..d91ca64
Binary files /dev/null and b/j11lib/groovy-console-2.5.7.jar differ
diff --git a/j11lib/groovy-datetime-2.5.7.jar b/j11lib/groovy-datetime-2.5.7.jar
new file mode 100644 (file)
index 0000000..55aa44b
Binary files /dev/null and b/j11lib/groovy-datetime-2.5.7.jar differ
diff --git a/j11lib/groovy-dateutil-2.5.7.jar b/j11lib/groovy-dateutil-2.5.7.jar
new file mode 100644 (file)
index 0000000..ccbddf1
Binary files /dev/null and b/j11lib/groovy-dateutil-2.5.7.jar differ
diff --git a/j11lib/groovy-docgenerator-2.5.7.jar b/j11lib/groovy-docgenerator-2.5.7.jar
new file mode 100644 (file)
index 0000000..a895272
Binary files /dev/null and b/j11lib/groovy-docgenerator-2.5.7.jar differ
diff --git a/j11lib/groovy-groovydoc-2.5.7.jar b/j11lib/groovy-groovydoc-2.5.7.jar
new file mode 100644 (file)
index 0000000..8917544
Binary files /dev/null and b/j11lib/groovy-groovydoc-2.5.7.jar differ
diff --git a/j11lib/groovy-groovysh-2.5.7.jar b/j11lib/groovy-groovysh-2.5.7.jar
new file mode 100644 (file)
index 0000000..2683ab1
Binary files /dev/null and b/j11lib/groovy-groovysh-2.5.7.jar differ
diff --git a/j11lib/groovy-jaxb-2.5.7.jar b/j11lib/groovy-jaxb-2.5.7.jar
new file mode 100644 (file)
index 0000000..879ffb4
Binary files /dev/null and b/j11lib/groovy-jaxb-2.5.7.jar differ
diff --git a/j11lib/groovy-jmx-2.5.7.jar b/j11lib/groovy-jmx-2.5.7.jar
new file mode 100644 (file)
index 0000000..5d8e104
Binary files /dev/null and b/j11lib/groovy-jmx-2.5.7.jar differ
diff --git a/j11lib/groovy-json-2.5.7.jar b/j11lib/groovy-json-2.5.7.jar
new file mode 100644 (file)
index 0000000..b65d2bb
Binary files /dev/null and b/j11lib/groovy-json-2.5.7.jar differ
diff --git a/j11lib/groovy-json-direct-2.5.7.jar b/j11lib/groovy-json-direct-2.5.7.jar
new file mode 100644 (file)
index 0000000..2f16d68
Binary files /dev/null and b/j11lib/groovy-json-direct-2.5.7.jar differ
diff --git a/j11lib/groovy-jsr223-2.5.7.jar b/j11lib/groovy-jsr223-2.5.7.jar
new file mode 100644 (file)
index 0000000..ff4c7db
Binary files /dev/null and b/j11lib/groovy-jsr223-2.5.7.jar differ
diff --git a/j11lib/groovy-macro-2.5.7.jar b/j11lib/groovy-macro-2.5.7.jar
new file mode 100644 (file)
index 0000000..accf49e
Binary files /dev/null and b/j11lib/groovy-macro-2.5.7.jar differ
diff --git a/j11lib/groovy-nio-2.5.7.jar b/j11lib/groovy-nio-2.5.7.jar
new file mode 100644 (file)
index 0000000..0273f0c
Binary files /dev/null and b/j11lib/groovy-nio-2.5.7.jar differ
diff --git a/j11lib/groovy-servlet-2.5.7.jar b/j11lib/groovy-servlet-2.5.7.jar
new file mode 100644 (file)
index 0000000..6145239
Binary files /dev/null and b/j11lib/groovy-servlet-2.5.7.jar differ
diff --git a/j11lib/groovy-sql-2.5.7.jar b/j11lib/groovy-sql-2.5.7.jar
new file mode 100644 (file)
index 0000000..a76f952
Binary files /dev/null and b/j11lib/groovy-sql-2.5.7.jar differ
diff --git a/j11lib/groovy-swing-2.5.7.jar b/j11lib/groovy-swing-2.5.7.jar
new file mode 100644 (file)
index 0000000..3864019
Binary files /dev/null and b/j11lib/groovy-swing-2.5.7.jar differ
diff --git a/j11lib/groovy-templates-2.5.7.jar b/j11lib/groovy-templates-2.5.7.jar
new file mode 100644 (file)
index 0000000..74d731b
Binary files /dev/null and b/j11lib/groovy-templates-2.5.7.jar differ
diff --git a/j11lib/groovy-test-2.5.7.jar b/j11lib/groovy-test-2.5.7.jar
new file mode 100644 (file)
index 0000000..9b79e94
Binary files /dev/null and b/j11lib/groovy-test-2.5.7.jar differ
diff --git a/j11lib/groovy-test-junit5-2.5.7.jar b/j11lib/groovy-test-junit5-2.5.7.jar
new file mode 100644 (file)
index 0000000..95815ff
Binary files /dev/null and b/j11lib/groovy-test-junit5-2.5.7.jar differ
diff --git a/j11lib/groovy-testng-2.5.7.jar b/j11lib/groovy-testng-2.5.7.jar
new file mode 100644 (file)
index 0000000..77884fa
Binary files /dev/null and b/j11lib/groovy-testng-2.5.7.jar differ
diff --git a/j11lib/groovy-xml-2.5.7.jar b/j11lib/groovy-xml-2.5.7.jar
new file mode 100644 (file)
index 0000000..034cdf9
Binary files /dev/null and b/j11lib/groovy-xml-2.5.7.jar differ
diff --git a/j11lib/istack-commons-runtime.jar b/j11lib/istack-commons-runtime.jar
new file mode 100644 (file)
index 0000000..2fe5b82
Binary files /dev/null and b/j11lib/istack-commons-runtime.jar differ
similarity index 100%
rename from lib/java-json.jar
rename to j11lib/java-json.jar
diff --git a/j11lib/javax.activation-api-1.2.0.jar b/j11lib/javax.activation-api-1.2.0.jar
new file mode 100644 (file)
index 0000000..986c365
Binary files /dev/null and b/j11lib/javax.activation-api-1.2.0.jar differ
diff --git a/j11lib/javax.annotation-api-1.3.2.jar b/j11lib/javax.annotation-api-1.3.2.jar
new file mode 100644 (file)
index 0000000..a8a470a
Binary files /dev/null and b/j11lib/javax.annotation-api-1.3.2.jar differ
diff --git a/j11lib/javax.jws-api-1.1.jar b/j11lib/javax.jws-api-1.1.jar
new file mode 100644 (file)
index 0000000..d4c06d2
Binary files /dev/null and b/j11lib/javax.jws-api-1.1.jar differ
diff --git a/j11lib/javax.servlet-api-4.0.1.jar b/j11lib/javax.servlet-api-4.0.1.jar
new file mode 100644 (file)
index 0000000..844ec7f
Binary files /dev/null and b/j11lib/javax.servlet-api-4.0.1.jar differ
diff --git a/j11lib/javax.xml.rpc-api-1.1.2.jar b/j11lib/javax.xml.rpc-api-1.1.2.jar
new file mode 100644 (file)
index 0000000..61ac294
Binary files /dev/null and b/j11lib/javax.xml.rpc-api-1.1.2.jar differ
diff --git a/j11lib/javax.xml.soap-api-1.4.0.jar b/j11lib/javax.xml.soap-api-1.4.0.jar
new file mode 100644 (file)
index 0000000..c47a3b0
Binary files /dev/null and b/j11lib/javax.xml.soap-api-1.4.0.jar differ
diff --git a/j11lib/jaxb-api-2.3.1.jar b/j11lib/jaxb-api-2.3.1.jar
new file mode 100644 (file)
index 0000000..4565865
Binary files /dev/null and b/j11lib/jaxb-api-2.3.1.jar differ
diff --git a/j11lib/jaxb-runtime-2.3.2.jar b/j11lib/jaxb-runtime-2.3.2.jar
new file mode 100644 (file)
index 0000000..62f8719
Binary files /dev/null and b/j11lib/jaxb-runtime-2.3.2.jar differ
diff --git a/j11lib/jaxws-api-2.3.1.jar b/j11lib/jaxws-api-2.3.1.jar
new file mode 100644 (file)
index 0000000..806c0e1
Binary files /dev/null and b/j11lib/jaxws-api-2.3.1.jar differ
diff --git a/j11lib/jaxws-rt-java9.jar b/j11lib/jaxws-rt-java9.jar
new file mode 100644 (file)
index 0000000..f380d18
Binary files /dev/null and b/j11lib/jaxws-rt-java9.jar differ
similarity index 82%
rename from lib/jersey-client-1.19.jar
rename to j11lib/jersey-client-1.19.4.jar
index c9e0f56..0588ee2 100644 (file)
Binary files a/lib/jersey-client-1.19.jar and b/j11lib/jersey-client-1.19.4.jar differ
similarity index 85%
rename from lib/jersey-core-1.19.jar
rename to j11lib/jersey-core-1.19.4.jar
index 92feb63..e800c60 100644 (file)
Binary files a/lib/jersey-core-1.19.jar and b/j11lib/jersey-core-1.19.4.jar differ
similarity index 85%
rename from lib/jersey-json-1.19.jar
rename to j11lib/jersey-json-1.19.4.jar
index b609411..c79271b 100644 (file)
Binary files a/lib/jersey-json-1.19.jar and b/j11lib/jersey-json-1.19.4.jar differ
similarity index 100%
rename from lib/jfreesvg-2.1.jar
rename to j11lib/jfreesvg-2.1.jar
similarity index 100%
rename from lib/jhall.jar
rename to j11lib/jhall.jar
similarity index 100%
rename from lib/jsoup-1.8.1.jar
rename to j11lib/jsoup-1.8.1.jar
diff --git a/j11lib/mail-MODULE.jar b/j11lib/mail-MODULE.jar
new file mode 100644 (file)
index 0000000..367ca9e
Binary files /dev/null and b/j11lib/mail-MODULE.jar differ
diff --git a/j11lib/mimepull-1.9.11.jar b/j11lib/mimepull-1.9.11.jar
new file mode 100644 (file)
index 0000000..99a4aba
Binary files /dev/null and b/j11lib/mimepull-1.9.11.jar differ
diff --git a/j11lib/policy-2.7.6.jar b/j11lib/policy-2.7.6.jar
new file mode 100644 (file)
index 0000000..56b0e31
Binary files /dev/null and b/j11lib/policy-2.7.6.jar differ
similarity index 100%
rename from lib/regex.jar
rename to j11lib/regex.jar
diff --git a/j11lib/saaj-impl.jar b/j11lib/saaj-impl.jar
new file mode 100644 (file)
index 0000000..7eda25c
Binary files /dev/null and b/j11lib/saaj-impl.jar differ
diff --git a/j11lib/slf4j-api-1.7.26.jar b/j11lib/slf4j-api-1.7.26.jar
new file mode 100644 (file)
index 0000000..d2f27ac
Binary files /dev/null and b/j11lib/slf4j-api-1.7.26.jar differ
diff --git a/j11lib/slf4j-log4j12-1.7.26.jar b/j11lib/slf4j-log4j12-1.7.26.jar
new file mode 100644 (file)
index 0000000..aed1195
Binary files /dev/null and b/j11lib/slf4j-log4j12-1.7.26.jar differ
diff --git a/j11lib/stax-ex-1.8.1.jar b/j11lib/stax-ex-1.8.1.jar
new file mode 100644 (file)
index 0000000..a200db5
Binary files /dev/null and b/j11lib/stax-ex-1.8.1.jar differ
diff --git a/j11lib/stax2-api-4.2.jar b/j11lib/stax2-api-4.2.jar
new file mode 100644 (file)
index 0000000..668dd42
Binary files /dev/null and b/j11lib/stax2-api-4.2.jar differ
diff --git a/j11lib/streambuffer-1.5.7.jar b/j11lib/streambuffer-1.5.7.jar
new file mode 100644 (file)
index 0000000..efc9a68
Binary files /dev/null and b/j11lib/streambuffer-1.5.7.jar differ
diff --git a/j11lib/txw2-2.3.2.jar b/j11lib/txw2-2.3.2.jar
new file mode 100644 (file)
index 0000000..0d5ac01
Binary files /dev/null and b/j11lib/txw2-2.3.2.jar differ
diff --git a/j11lib/wsdl4j-1.6.3.jar b/j11lib/wsdl4j-1.6.3.jar
new file mode 100644 (file)
index 0000000..b9c10b9
Binary files /dev/null and b/j11lib/wsdl4j-1.6.3.jar differ
similarity index 100%
rename from lib/xercesImpl.jar
rename to j11lib/xercesImpl.jar
diff --git a/j11mod/FastInfoset.jar b/j11mod/FastInfoset.jar
new file mode 100644 (file)
index 0000000..2671209
Binary files /dev/null and b/j11mod/FastInfoset.jar differ
diff --git a/j11mod/getdown-launcher.jar b/j11mod/getdown-launcher.jar
new file mode 100644 (file)
index 0000000..187694e
Binary files /dev/null and b/j11mod/getdown-launcher.jar differ
diff --git a/j11mod/istack-commons-runtime.jar b/j11mod/istack-commons-runtime.jar
new file mode 100644 (file)
index 0000000..2fe5b82
Binary files /dev/null and b/j11mod/istack-commons-runtime.jar differ
diff --git a/j11mod/javax.activation-MODULE.jar b/j11mod/javax.activation-MODULE.jar
new file mode 100644 (file)
index 0000000..b764265
Binary files /dev/null and b/j11mod/javax.activation-MODULE.jar differ
diff --git a/j11mod/javax.annotation-api-MODULE.jar b/j11mod/javax.annotation-api-MODULE.jar
new file mode 100644 (file)
index 0000000..a0f12ad
Binary files /dev/null and b/j11mod/javax.annotation-api-MODULE.jar differ
diff --git a/j11mod/javax.jws-api-1.1.jar b/j11mod/javax.jws-api-1.1.jar
new file mode 100644 (file)
index 0000000..d4c06d2
Binary files /dev/null and b/j11mod/javax.jws-api-1.1.jar differ
diff --git a/j11mod/javax.servlet-api-MODULE.jar b/j11mod/javax.servlet-api-MODULE.jar
new file mode 100644 (file)
index 0000000..9241563
Binary files /dev/null and b/j11mod/javax.servlet-api-MODULE.jar differ
diff --git a/j11mod/javax.ws.rs-api-2.1.1.jar b/j11mod/javax.ws.rs-api-2.1.1.jar
new file mode 100644 (file)
index 0000000..3eabbf0
Binary files /dev/null and b/j11mod/javax.ws.rs-api-2.1.1.jar differ
diff --git a/j11mod/javax.xml.rpc-api-1.1.2.jar b/j11mod/javax.xml.rpc-api-1.1.2.jar
new file mode 100644 (file)
index 0000000..61ac294
Binary files /dev/null and b/j11mod/javax.xml.rpc-api-1.1.2.jar differ
diff --git a/j11mod/javax.xml.soap-api.jar b/j11mod/javax.xml.soap-api.jar
new file mode 100644 (file)
index 0000000..c47a3b0
Binary files /dev/null and b/j11mod/javax.xml.soap-api.jar differ
diff --git a/j11mod/jaxb-api-java9.jar b/j11mod/jaxb-api-java9.jar
new file mode 100644 (file)
index 0000000..41de3c1
Binary files /dev/null and b/j11mod/jaxb-api-java9.jar differ
diff --git a/j11mod/jaxb-runtime.jar b/j11mod/jaxb-runtime.jar
new file mode 100644 (file)
index 0000000..0b9ef67
Binary files /dev/null and b/j11mod/jaxb-runtime.jar differ
diff --git a/j11mod/jaxws-api.jar b/j11mod/jaxws-api.jar
new file mode 100644 (file)
index 0000000..806c0e1
Binary files /dev/null and b/j11mod/jaxws-api.jar differ
diff --git a/j11mod/mimepull.jar b/j11mod/mimepull.jar
new file mode 100644 (file)
index 0000000..9007c56
Binary files /dev/null and b/j11mod/mimepull.jar differ
diff --git a/j11mod/policy.jar b/j11mod/policy.jar
new file mode 100644 (file)
index 0000000..8a2ef0f
Binary files /dev/null and b/j11mod/policy.jar differ
diff --git a/j11mod/saaj-impl.jar b/j11mod/saaj-impl.jar
new file mode 100644 (file)
index 0000000..7eda25c
Binary files /dev/null and b/j11mod/saaj-impl.jar differ
diff --git a/j11mod/stax-ex.jar b/j11mod/stax-ex.jar
new file mode 100644 (file)
index 0000000..5e60637
Binary files /dev/null and b/j11mod/stax-ex.jar differ
diff --git a/j11mod/stax2-api-MODULE.jar b/j11mod/stax2-api-MODULE.jar
new file mode 100644 (file)
index 0000000..eff6586
Binary files /dev/null and b/j11mod/stax2-api-MODULE.jar differ
diff --git a/j11mod/streambuffer.jar b/j11mod/streambuffer.jar
new file mode 100644 (file)
index 0000000..a8f9640
Binary files /dev/null and b/j11mod/streambuffer.jar differ
diff --git a/j11mod/txw2.jar b/j11mod/txw2.jar
new file mode 100644 (file)
index 0000000..75ed519
Binary files /dev/null and b/j11mod/txw2.jar differ
diff --git a/j11mod/wsdl4j-MODULE.jar b/j11mod/wsdl4j-MODULE.jar
new file mode 100644 (file)
index 0000000..0f9e45c
Binary files /dev/null and b/j11mod/wsdl4j-MODULE.jar differ
diff --git a/j8lib/JGoogleAnalytics_0.3.jar b/j8lib/JGoogleAnalytics_0.3.jar
new file mode 100644 (file)
index 0000000..0dbc98c
Binary files /dev/null and b/j8lib/JGoogleAnalytics_0.3.jar differ
diff --git a/j8lib/VARNAv3-93.jar b/j8lib/VARNAv3-93.jar
new file mode 100644 (file)
index 0000000..9d41f6b
Binary files /dev/null and b/j8lib/VARNAv3-93.jar differ
diff --git a/j8lib/VAqua5-patch.jar b/j8lib/VAqua5-patch.jar
new file mode 100644 (file)
index 0000000..7b5c27b
Binary files /dev/null and b/j8lib/VAqua5-patch.jar differ
similarity index 100%
rename from lib/activation.jar
rename to j8lib/activation.jar
diff --git a/j8lib/apache-mime4j-0.6.jar b/j8lib/apache-mime4j-0.6.jar
new file mode 100644 (file)
index 0000000..1d2282c
Binary files /dev/null and b/j8lib/apache-mime4j-0.6.jar differ
diff --git a/j8lib/axis.jar b/j8lib/axis.jar
new file mode 100755 (executable)
index 0000000..90bb798
Binary files /dev/null and b/j8lib/axis.jar differ
diff --git a/j8lib/biojava-core-4.1.0.jar b/j8lib/biojava-core-4.1.0.jar
new file mode 100644 (file)
index 0000000..5a09c1f
Binary files /dev/null and b/j8lib/biojava-core-4.1.0.jar differ
diff --git a/j8lib/biojava-ontology-4.1.0.jar b/j8lib/biojava-ontology-4.1.0.jar
new file mode 100644 (file)
index 0000000..80737d5
Binary files /dev/null and b/j8lib/biojava-ontology-4.1.0.jar differ
diff --git a/j8lib/commons-codec-1.3.jar b/j8lib/commons-codec-1.3.jar
new file mode 100644 (file)
index 0000000..957b675
Binary files /dev/null and b/j8lib/commons-codec-1.3.jar differ
diff --git a/j8lib/commons-compress-1.18.jar b/j8lib/commons-compress-1.18.jar
new file mode 100644 (file)
index 0000000..e401046
Binary files /dev/null and b/j8lib/commons-compress-1.18.jar differ
diff --git a/j8lib/commons-discovery.jar b/j8lib/commons-discovery.jar
new file mode 100755 (executable)
index 0000000..b885548
Binary files /dev/null and b/j8lib/commons-discovery.jar differ
diff --git a/j8lib/commons-logging-1.1.1.jar b/j8lib/commons-logging-1.1.1.jar
new file mode 100644 (file)
index 0000000..1deef14
Binary files /dev/null and b/j8lib/commons-logging-1.1.1.jar differ
diff --git a/j8lib/getdown-core.jar b/j8lib/getdown-core.jar
new file mode 100644 (file)
index 0000000..bbe1476
Binary files /dev/null and b/j8lib/getdown-core.jar differ
diff --git a/j8lib/htsjdk-2.12.0.jar b/j8lib/htsjdk-2.12.0.jar
new file mode 100644 (file)
index 0000000..1df12b2
Binary files /dev/null and b/j8lib/htsjdk-2.12.0.jar differ
diff --git a/j8lib/httpclient-4.0.3.jar b/j8lib/httpclient-4.0.3.jar
new file mode 100644 (file)
index 0000000..fd0d377
Binary files /dev/null and b/j8lib/httpclient-4.0.3.jar differ
diff --git a/j8lib/httpcore-4.0.1.jar b/j8lib/httpcore-4.0.1.jar
new file mode 100644 (file)
index 0000000..4aef35e
Binary files /dev/null and b/j8lib/httpcore-4.0.1.jar differ
diff --git a/j8lib/httpmime-4.0.3.jar b/j8lib/httpmime-4.0.3.jar
new file mode 100644 (file)
index 0000000..0dfd331
Binary files /dev/null and b/j8lib/httpmime-4.0.3.jar differ
diff --git a/j8lib/intervalstore-v1.0.jar b/j8lib/intervalstore-v1.0.jar
new file mode 100644 (file)
index 0000000..4f6101c
Binary files /dev/null and b/j8lib/intervalstore-v1.0.jar differ
diff --git a/j8lib/jabaws-min-client-2.2.0.jar b/j8lib/jabaws-min-client-2.2.0.jar
new file mode 100644 (file)
index 0000000..37426c3
Binary files /dev/null and b/j8lib/jabaws-min-client-2.2.0.jar differ
diff --git a/j8lib/java-json.jar b/j8lib/java-json.jar
new file mode 100755 (executable)
index 0000000..2f211e3
Binary files /dev/null and b/j8lib/java-json.jar differ
similarity index 100%
rename from lib/jaxrpc.jar
rename to j8lib/jaxrpc.jar
diff --git a/j8lib/jersey-client-1.19.4.jar b/j8lib/jersey-client-1.19.4.jar
new file mode 100644 (file)
index 0000000..0588ee2
Binary files /dev/null and b/j8lib/jersey-client-1.19.4.jar differ
diff --git a/j8lib/jersey-core-1.19.4.jar b/j8lib/jersey-core-1.19.4.jar
new file mode 100644 (file)
index 0000000..e800c60
Binary files /dev/null and b/j8lib/jersey-core-1.19.4.jar differ
diff --git a/j8lib/jersey-json-1.19.4.jar b/j8lib/jersey-json-1.19.4.jar
new file mode 100644 (file)
index 0000000..c79271b
Binary files /dev/null and b/j8lib/jersey-json-1.19.4.jar differ
diff --git a/j8lib/jetty-http-9.2.10.v20150310.jar b/j8lib/jetty-http-9.2.10.v20150310.jar
new file mode 100644 (file)
index 0000000..15aff51
Binary files /dev/null and b/j8lib/jetty-http-9.2.10.v20150310.jar differ
diff --git a/j8lib/jetty-io-9.2.10.v20150310.jar b/j8lib/jetty-io-9.2.10.v20150310.jar
new file mode 100644 (file)
index 0000000..56cee2c
Binary files /dev/null and b/j8lib/jetty-io-9.2.10.v20150310.jar differ
diff --git a/j8lib/jetty-server-9.2.10.v20150310.jar b/j8lib/jetty-server-9.2.10.v20150310.jar
new file mode 100644 (file)
index 0000000..815cb08
Binary files /dev/null and b/j8lib/jetty-server-9.2.10.v20150310.jar differ
diff --git a/j8lib/jetty-util-9.2.10.v20150310.jar b/j8lib/jetty-util-9.2.10.v20150310.jar
new file mode 100644 (file)
index 0000000..fe27758
Binary files /dev/null and b/j8lib/jetty-util-9.2.10.v20150310.jar differ
diff --git a/j8lib/jfreesvg-2.1.jar b/j8lib/jfreesvg-2.1.jar
new file mode 100644 (file)
index 0000000..91d453c
Binary files /dev/null and b/j8lib/jfreesvg-2.1.jar differ
diff --git a/j8lib/jhall.jar b/j8lib/jhall.jar
new file mode 100755 (executable)
index 0000000..ff08af0
Binary files /dev/null and b/j8lib/jhall.jar differ
diff --git a/j8lib/json_simple-1.1.jar b/j8lib/json_simple-1.1.jar
new file mode 100644 (file)
index 0000000..f395f41
Binary files /dev/null and b/j8lib/json_simple-1.1.jar differ
diff --git a/j8lib/jsoup-1.8.1.jar b/j8lib/jsoup-1.8.1.jar
new file mode 100644 (file)
index 0000000..ae717d4
Binary files /dev/null and b/j8lib/jsoup-1.8.1.jar differ
diff --git a/j8lib/jsr311-api-1.1.1.jar b/j8lib/jsr311-api-1.1.1.jar
new file mode 100644 (file)
index 0000000..ec8bc81
Binary files /dev/null and b/j8lib/jsr311-api-1.1.1.jar differ
diff --git a/j8lib/jswingreader-0.3.jar b/j8lib/jswingreader-0.3.jar
new file mode 100644 (file)
index 0000000..c632130
Binary files /dev/null and b/j8lib/jswingreader-0.3.jar differ
diff --git a/j8lib/libquaqua-8.0.jnilib.jar b/j8lib/libquaqua-8.0.jnilib.jar
new file mode 100644 (file)
index 0000000..79383b0
Binary files /dev/null and b/j8lib/libquaqua-8.0.jnilib.jar differ
diff --git a/j8lib/libquaqua64-8.0.jnilib.jar b/j8lib/libquaqua64-8.0.jnilib.jar
new file mode 100644 (file)
index 0000000..efb46a4
Binary files /dev/null and b/j8lib/libquaqua64-8.0.jnilib.jar differ
diff --git a/j8lib/log4j-to-slf4j-2.0-rc2.jar b/j8lib/log4j-to-slf4j-2.0-rc2.jar
new file mode 100644 (file)
index 0000000..4bbf54a
Binary files /dev/null and b/j8lib/log4j-to-slf4j-2.0-rc2.jar differ
similarity index 100%
rename from lib/mail.jar
rename to j8lib/mail.jar
diff --git a/j8lib/miglayout-4.0-swing.jar b/j8lib/miglayout-4.0-swing.jar
new file mode 100644 (file)
index 0000000..7b6a22c
Binary files /dev/null and b/j8lib/miglayout-4.0-swing.jar differ
diff --git a/j8lib/quaqua-filechooser-only-8.0.jar b/j8lib/quaqua-filechooser-only-8.0.jar
new file mode 100644 (file)
index 0000000..182e0da
Binary files /dev/null and b/j8lib/quaqua-filechooser-only-8.0.jar differ
diff --git a/j8lib/regex.jar b/j8lib/regex.jar
new file mode 100755 (executable)
index 0000000..3e29d22
Binary files /dev/null and b/j8lib/regex.jar differ
similarity index 100%
rename from lib/saaj.jar
rename to j8lib/saaj.jar
diff --git a/j8lib/vamsas-client.jar b/j8lib/vamsas-client.jar
new file mode 100644 (file)
index 0000000..2634954
Binary files /dev/null and b/j8lib/vamsas-client.jar differ
similarity index 100%
rename from lib/wsdl4j.jar
rename to j8lib/wsdl4j.jar
diff --git a/j8lib/xercesImpl.jar b/j8lib/xercesImpl.jar
new file mode 100644 (file)
index 0000000..0aaa990
Binary files /dev/null and b/j8lib/xercesImpl.jar differ
similarity index 100%
rename from lib/xml-apis.jar
rename to j8lib/xml-apis.jar
diff --git a/lib/.cvsignore b/lib/.cvsignore
deleted file mode 100644 (file)
index 5feaa93..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-NOBACK
-XmlSchema-SNAPSHOT.jar
-annogen-0.1.0.jar
-axiom-api-1.0-RC2.jar
-axiom-dom-1.0-RC2.jar
-axiom-impl-1.0-RC2.jar
-axis-wsdl4j-1.2.jar
-axis.jar.bak
-axis.jar.commons
-axis2-adb-1.0-RC2.jar
-axis2-kernel-1.0-RC2.jar
-backport-util-concurrent-2.0_01_pd.jar
-backport-util-concurrent-2.1.jar
-castor.zip
-commons-codec-1.2.jar
-commons-codec-1.3.jar
-commons-fileupload-1.0.jar
-commons-httpclient-3.0.jar
-commons-logging-1.0.4.jar
-geronimo-spec-activation-1.0.2-rc4.jar
-geronimo-spec-javamail-1.3.1-rc5.jar
-geronimo-spec-jms-1.1-rc4.jar
-jaxen-1.1-beta-7.jar
-jhall.jar.bak
-log4j-1.2.13.jar
-maven-itest-plugin-1.0.jar
-phylip-client-axis-1.3.jar
-policy-1.0.1-RC.jar
-policy-1.0.jar
-servletapi-2.3.jar
-stax-api-1.0.jar
-wstx-asl-2.8.2.jar
-wstx-asl-2.9.2.jar
-xercesImpl.jar.bak
-xml-apis.jar.bak
diff --git a/modules b/modules
new file mode 100644 (file)
index 0000000..183a26f
--- /dev/null
+++ b/modules
@@ -0,0 +1 @@
+com.sun.istack.runtime,com.sun.xml.bind,com.sun.xml.fastinfoset,com.sun.xml.streambuffer,com.sun.xml.txw2,com.sun.xml.ws.policy,java.activation,java.annotation,java.base,java.compiler,java.datatransfer,java.desktop,java.logging,java.management,java.management.rmi,java.naming,java.prefs,java.rmi,java.scripting,java.security.sasl,java.sql,java.xml,java.xml.bind,java.xml.soap,java.xml.ws,javax.jws,jdk.httpserver,jdk.jsobject,jdk.unsupported,jdk.xml.dom,org.jvnet.mimepull,org.jvnet.staxex,javax.servlet.api,java.ws.rs
diff --git a/resources/images/Jalview_Logo_small_with_border.png b/resources/images/Jalview_Logo_small_with_border.png
new file mode 100644 (file)
index 0000000..216121c
Binary files /dev/null and b/resources/images/Jalview_Logo_small_with_border.png differ
old mode 100755 (executable)
new mode 100644 (file)
diff --git a/resources/images/jalview_logo_background_fade-640x480.png b/resources/images/jalview_logo_background_fade-640x480.png
new file mode 100644 (file)
index 0000000..4b65200
Binary files /dev/null and b/resources/images/jalview_logo_background_fade-640x480.png differ
diff --git a/resources/images/jalview_logos.icns b/resources/images/jalview_logos.icns
new file mode 100644 (file)
index 0000000..6c2ee9a
Binary files /dev/null and b/resources/images/jalview_logos.icns differ
diff --git a/resources/images/jalview_logos.ico b/resources/images/jalview_logos.ico
new file mode 100644 (file)
index 0000000..ba2ca82
Binary files /dev/null and b/resources/images/jalview_logos.ico differ
diff --git a/resources/images/jetset_jalview_splash.gif b/resources/images/jetset_jalview_splash.gif
new file mode 100644 (file)
index 0000000..cc144b1
Binary files /dev/null and b/resources/images/jetset_jalview_splash.gif differ
diff --git a/resources/images/jetset_jalview_splash.png b/resources/images/jetset_jalview_splash.png
new file mode 100644 (file)
index 0000000..3ce1f46
Binary files /dev/null and b/resources/images/jetset_jalview_splash.png differ
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
index 6c11705..b137a88 100644 (file)
@@ -32,6 +32,7 @@ action.load_project = Load Project
 action.save_project = Save Project
 action.save_project_as = Save Project as...
 action.quit = Quit
+label.quit_jalview = Quit Jalview?
 action.expand_views = Expand Views
 action.gather_views = Gather Views
 action.page_setup = Page Setup...
@@ -994,7 +995,8 @@ label.toggled = Toggled
 label.marked = Marked
 label.containing = containing
 label.not_containing = not containing
-label.no_feature_of_type_found = No features of type {0} found.
+label.no_feature_of_type_found = No features of type {0} found
+label.no_feature_found_selection = No features of type {0} found in selection
 label.submission_params = Submission {0}
 label.empty_alignment_job = Empty Alignment Job
 label.add_new_sbrs_service = Add a new Simple Bioinformatics Rest Service
@@ -1219,9 +1221,6 @@ label.structure_chooser_filter_time = Structure Chooser - Filter time ({0})
 label.structure_chooser_no_of_structures = Structure Chooser - {0} Found ({1})
 info.no_pdb_entry_found_for = No PDB entry found for {0}
 exception.unable_to_detect_internet_connection = Jalview is unable to detect an internet connection
-exception.fts_rest_service_no_longer_available = {0} rest services no longer available!
-exception.resource_not_be_found = The requested resource could not be found
-exception.fts_server_error = There seems to be an error from the {0} server
 exception.fts_server_unreachable = Jalview is unable to reach the {0} server. \nPlease ensure that you are connected to the internet and try again.
 label.nw_mapping = Needleman & Wunsch Alignment
 label.sifts_mapping = SIFTs Mapping
@@ -1237,8 +1236,6 @@ action.next_page= >>
 action.prev_page= << 
 label.next_page_tooltip=Next Page
 label.prev_page_tooltip=Previous Page
-exception.bad_request=Bad request. There is a problem with your input.
-exception.service_not_available=Service not available. The server is being updated, try again later.
 status.launching_3d_structure_viewer = Launching 3D Structure viewer...
 status.fetching_3d_structures_for_selected_entries = Fetching 3D Structures for selected entries...
 status.fetching_dbrefs_for_sequences_without_valid_refs = Fetching db refs for {0} sequence(s) without valid db ref required for SIFTS mapping
@@ -1306,6 +1303,7 @@ label.numeric_required = The value should be numeric
 label.filter = Filter
 label.filters = Filters
 label.join_conditions = Join conditions with
+label.delete_condition = Delete this condition
 label.score = Score
 label.colour_by_label = Colour by label
 label.variable_colour = Variable colour...
@@ -1352,12 +1350,13 @@ label.append_to_filename = Append to filename (%n is replaced by the backup numb
 label.append_to_filename_tooltip = %n in the text will be replaced by the backup number. The text will appear after the filename. See the summary box above.
 label.index_digits = Number of digits to use for the backup number (%n)
 label.summary_of_backups_scheme = Summary of backup scheme
+label.scheme_examples = Scheme examples
 label.increment_index = Increase appended text numbers - newest file has largest number.
 label.reverse_roll = "Roll" appended text numbers - newest backup file is always number 1.
 label.keep_files = Deleting old backup files
 label.keep_all_backup_files = Do not delete old backup files
 label.keep_only_this_number_of_backup_files = Keep only this number of most recent backup files
-label.autodelete_old_backup_files = Autodelete old backup files:
+label.autodelete_old_backup_files = Auto-delete old backup files:
 label.always_ask = Always ask
 label.auto_delete = Automatically delete
 label.filename = filename
@@ -1367,10 +1366,18 @@ label.configuration = Configuration
 label.configure_feature_tooltip = Click to configure variable colour or filters
 label.schemes = Schemes
 label.customise = Customise
+label.custom = Custom
 label.default = Default
 label.single_file = Single backup
 label.keep_all_versions = Keep all versions
 label.rolled_backups = Rolled backup files
+label.customise_description = Select Customise, make changes, and click on OK to save your own custom scheme
+label.custom_description = Your own saved scheme
+label.default_description = Keep the last three versions of the file
+label.single_file_description = Keep the last version of the file
+label.keep_all_versions_description = Keep all previous versions of the file
+label.rolled_backups_description = Keep the last nine versions of the file from _bak.1 (newest) to _bak.9 (oldest)
+label.cancel_changes_description = Cancel changes made to your last saved Custom scheme
 label.previously_saved_scheme = Previously saved scheme
 label.no_backup_files = NO BACKUP FILES
 label.include_backup_files = Include backup files
index 8f91dc7..5008c62 100644 (file)
@@ -32,6 +32,7 @@ action.load_project = Cargar proyecto
 action.save_project = Guardar proyecto
 action.save_project_as = Guardar proyecto como...
 action.quit = Salir
+label.quit_jalview = Salir de Jalview?
 action.expand_views = Expandir vistas
 action.gather_views = Capturar vistas
 action.page_setup = Configuración de la página
@@ -919,7 +920,8 @@ label.toggled = Invertida
 label.marked = Marcada
 label.containing = conteniendo
 label.not_containing = no conteniendo
-label.no_feature_of_type_found = No se han encontrado características del tipo {0}.
+label.no_feature_of_type_found = No se han encontrado características del tipo {0}
+label.no_feature_found_selection = No se han encontrado características del tipo {0} en la región seleccionada
 label.submission_params = Envío {0}
 label.empty_alignment_job = Trabajo de alineamiento vacío
 label.add_new_sbrs_service = Añadir un nuevo SBRS
@@ -1193,7 +1195,6 @@ tooltip.rnalifold_settings=Modificar la configuraci
 label.show_selected_annotations=Mostrar anotaciones seleccionadas
 status.colouring_chimera=Coloreando Chimera
 label.configure_displayed_columns=Configurar Columnas Mostradas
-exception.resource_not_be_found=El recurso solicitado no se ha encontrado
 label.aacon_calculations=cálculos AACon
 label.pdb_web-service_error=Error de servicio web PDB
 exception.unable_to_detect_internet_connection=Jalview no puede detectar una conexión a Internet
@@ -1218,12 +1219,10 @@ exception.fts_server_unreachable=Jalview no puede conectar con el servidor {0}.
 exception.outofmemory_loading_mmcif_file=Sin memoria al cargar el fichero mmCIF
 label.hide_columns_not_containing=Ocultar las columnas que no contengan
 label.pdb_sequence_fetcher=Recuperador de secuencias PDB
-exception.fts_server_error=Parece que hay un error desde el servidor {0}
-exception.service_not_available=Servicio no disponible. El servidor se está actualizando, vuelva a intentarlo más tarde.
+status.waiting_for_user_to_select_output_file=Esperando que el usuario seleccione el fichero {0}
 action.prev_page=<< 
 status.cancelled_image_export_operation=Operación de exportación {0} cancelada
 label.couldnt_run_groovy_script=No se ha podido ejecutar el script Groovy
-exception.bad_request=Solicitud incorrecta. Hay un problema con su entrada.
 label.run_groovy=Ejecutar script Groovy desde la consola
 action.next_page=>> 
 label.uniprot_sequence_fetcher=Recuperador de secuencias UniProt
@@ -1239,7 +1238,6 @@ label.next_page_tooltip=P
 label.sifts_mapping=Mapeado SIFTs
 label.mapping_method=Método de mapeo de secuencia \u27F7 estructura
 info.error_creating_file=Error al crear fichero {0}
-exception.fts_rest_service_no_longer_available= Servicios Rest {0} ya no están disponibles! 
 status.launching_3d_structure_viewer=Lanzando visualizador de estructura 3D...
 status.obtaining_mapping_with_sifts=Obteniendo mapeo por SIFTS
 status.fetching_3d_structures_for=Buscando la estructura 3D para {0}
@@ -1306,6 +1304,7 @@ label.matchCondition_ge = >=
 label.numeric_required = Valor numérico requerido
 label.filter = Filtro
 label.filters = Filtros
+label.delete_condition = Borrar esta condición
 label.join_conditions = Combinar condiciones con
 label.score = Puntuación
 label.colour_by_label = Colorear por texto
@@ -1353,6 +1352,7 @@ label.append_to_filename = Adjuntar texto (%n es reemplazado por el n
 label.append_to_filename_tooltip = %n en el texto será reemplazado por el número de respaldo. El texto será después del nombre del archivo. Vea el cuadro de resumen arriba.
 label.index_digits = Número de dígitos a utilizar para el número de respaldo.
 label.summary_of_backups_scheme = Resumen del esquema de copias de seguridad
+label.scheme_examples = Ejemplos de esquema
 label.increment_index = Aumente los números de texto adjuntos: el archivo más nuevo tiene el número más grande
 label.reverse_roll = Ciclos de texto adjuntos: el respaldo más reciente es siempre el número 1
 label.keep_files = Borrando los respaldos antiguos
@@ -1367,11 +1367,19 @@ label.braced_newest = (mas nuevo)
 label.configuration = Configuración
 label.configure_feature_tooltip = Haga clic para configurar el color o los filtros
 label.schemes = Esquemas
-label.customise = Personalizado
+label.customise = Personalizar
+label.custom = Personal
 label.default = Defecto
 label.single_file = Solo uno respaldo
 label.keep_all_versions = Mantener todas las versiones
 label.rolled_backups = Ciclos respaldos
+label.customise_description = Seleccione Personalizar, haga cambios y haga clic en OK para guardar su propio esquema personalizado
+label.custom_description = Tu propio esquema guardado
+label.default_description = Conserve las Ãºltimas tres versiones del archivo
+label.single_file_description = Conserve la Ãºltima versión del archivo
+label.keep_all_versions_description = Mantener todas las versiones anteriores del archivo
+label.rolled_backups_description = Mantenga las Ãºltimas nueve versiones del archivo desde _bak.1 (más reciente) a _bak.9 (más antigua)
+label.cancel_changes_description = Cancelar los cambios realizados en su Ãºltimo esquema personalizado guardado
 label.previously_saved_scheme = Esquema previamente guardado
 label.no_backup_files = NO ARCHIVOS DE RESPALDOS
 label.include_backup_files = Incluir archivos de respaldos
index 07dee98..1d2235e 100755 (executable)
                                                                </xs:documentation>
                                                        </xs:annotation>
                                                </xs:attribute>
+                                               <xs:attribute name="showComplementFeatures" type="xs:boolean" use="optional" default="false" />
+                                               <xs:attribute name="showComplementFeaturesOnTop" type="xs:boolean" use="optional" default="false" />
                                        </xs:complexType>
                                </xs:element>
                                <xs:element name="UserColours" minOccurs="0" maxOccurs="unbounded">
index 22b7aac..20c19dd 100755 (executable)
                                                        </xs:element>
                                                </xs:choice>
                                        </xs:sequence>
+                                       <xs:attribute name="mappingType" type="xs:string" use="optional">
+                                               <xs:annotation>
+                                                       <xs:documentation>Biotype of the mapping e.g. CdsToPeptide</xs:documentation>
+                                               </xs:annotation>
+                                       </xs:attribute>
                                </xs:extension>
                        </xs:complexContent>    
                </xs:complexType>
                                                                <xs:attribute name="source" type="xs:string"/>
                                                                <xs:attribute name="version" type="xs:string"/>
                                                                <xs:attribute name="accessionId" type="xs:string"/>
+                                                               <xs:attribute name="locus" type="xs:boolean" default="false">
+                                                                       <xs:annotation>
+                                                                               <xs:documentation>
+                                                                                       true for gene locus mapping, source=species, version=assembly, accession=chromosome
+                                                                               </xs:documentation>
+                                                                       </xs:annotation>
+                                                               </xs:attribute>
                                                        </xs:complexType>
                                                </xs:element>
                                        </xs:sequence>
                                                        </xs:documentation>
                                                </xs:annotation>
                                        </xs:attribute>
+                                       <xs:attribute name="biotype" type="xs:string" use="optional">
+                                               <xs:annotation>
+                                                       <xs:documentation>
+                                                               Biotype of the sequence (if known)
+                                                       </xs:documentation>
+                                               </xs:annotation>
+                                       </xs:attribute>
                                </xs:extension>
                        </xs:complexContent>
                </xs:complexType>
diff --git a/src/MCview/AppletPDBCanvas.java b/src/MCview/AppletPDBCanvas.java
new file mode 100644 (file)
index 0000000..c6553f8
--- /dev/null
@@ -0,0 +1,1264 @@
+/*
+ * 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 MCview;
+
+import jalview.analysis.AlignSeq;
+import jalview.appletgui.AlignmentPanel;
+import jalview.appletgui.FeatureRenderer;
+import jalview.appletgui.SequenceRenderer;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.StructureFile;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
+import jalview.structure.AtomSpec;
+import jalview.structure.StructureListener;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.InputEvent;import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Image;
+// JBPNote TODO: This class is quite noisy - needs proper log.info/log.debug
+import java.awt.Panel;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Vector;
+
+public class AppletPDBCanvas extends Panel
+        implements MouseListener, MouseMotionListener, StructureListener
+{
+
+  MCMatrix idmat = new MCMatrix(3, 3);
+
+  MCMatrix objmat = new MCMatrix(3, 3);
+
+  boolean redrawneeded = true;
+
+  int omx = 0;
+
+  int mx = 0;
+
+  int omy = 0;
+
+  int my = 0;
+
+  public StructureFile pdb;
+
+  PDBEntry pdbentry;
+
+  int bsize;
+
+  Image img;
+
+  Graphics ig;
+
+  Dimension prefsize;
+
+  float[] centre = new float[3];
+
+  float[] width = new float[3];
+
+  float maxwidth;
+
+  float scale;
+
+  String inStr;
+
+  String inType;
+
+  boolean bysequence = true;
+
+  boolean depthcue = true;
+
+  boolean wire = false;
+
+  boolean bymolecule = false;
+
+  boolean zbuffer = true;
+
+  boolean dragging;
+
+  int xstart;
+
+  int xend;
+
+  int ystart;
+
+  int yend;
+
+  int xmid;
+
+  int ymid;
+
+  Font font = new Font("Helvetica", Font.PLAIN, 10);
+
+  public SequenceI[] sequence;
+
+  final StringBuffer mappingDetails = new StringBuffer();
+
+  String appletToolTip = null;
+
+  int toolx, tooly;
+
+  PDBChain mainchain;
+
+  Vector<String> highlightRes;
+
+  boolean pdbAction = false;
+
+  Bond highlightBond1, highlightBond2;
+
+  boolean errorLoading = false;
+
+  boolean seqColoursReady = false;
+
+  FeatureRenderer fr;
+
+  AlignmentPanel ap;
+
+  StructureSelectionManager ssm;
+
+  public AppletPDBCanvas(PDBEntry pdbentry, SequenceI[] seq,
+          String[] chains, AlignmentPanel ap, DataSourceType protocol)
+
+  {
+    this.ap = ap;
+    this.pdbentry = pdbentry;
+    this.sequence = seq;
+
+    ssm = StructureSelectionManager
+            .getStructureSelectionManager(ap.av.applet);
+
+    try
+    {
+      pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol, null);
+
+      if (protocol == DataSourceType.PASTE)
+      {
+        pdbentry.setFile("INLINE" + pdb.getId());
+      }
+
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
+      return;
+    }
+
+    pdbentry.setId(pdb.getId());
+
+    ssm.addStructureViewerListener(this);
+
+    colourBySequence();
+
+    float max = -10;
+    int maxchain = -1;
+    int pdbstart = 0;
+    int pdbend = 0;
+    int seqstart = 0;
+    int seqend = 0;
+
+    // JUST DEAL WITH ONE SEQUENCE FOR NOW
+    SequenceI sequence = seq[0];
+
+    for (int i = 0; i < pdb.getChains().size(); i++)
+    {
+
+      mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
+              + pdb.getChains().elementAt(i).sequence
+                      .getSequenceAsString());
+      mappingDetails.append("\nNo of residues = "
+              + pdb.getChains().elementAt(i).residues.size() + "\n\n");
+
+      // Now lets compare the sequences to get
+      // the start and end points.
+      // Align the sequence to the pdb
+      // TODO: DNa/Pep switch
+      AlignSeq as = new AlignSeq(sequence,
+              pdb.getChains().elementAt(i).sequence,
+              pdb.getChains().elementAt(i).isNa ? AlignSeq.DNA
+                      : AlignSeq.PEP);
+      as.calcScoreMatrix();
+      as.traceAlignment();
+      PrintStream ps = new PrintStream(System.out)
+      {
+        @Override
+        public void print(String x)
+        {
+          mappingDetails.append(x);
+        }
+
+        @Override
+        public void println()
+        {
+          mappingDetails.append("\n");
+        }
+      };
+
+      as.printAlignment(ps);
+
+      if (as.maxscore > max)
+      {
+        max = as.maxscore;
+        maxchain = i;
+
+        pdbstart = as.seq2start;
+        pdbend = as.seq2end;
+        seqstart = as.seq1start + sequence.getStart() - 1;
+        seqend = as.seq1end + sequence.getEnd() - 1;
+      }
+
+      mappingDetails.append("\nPDB start/end " + pdbstart + " " + pdbend);
+      mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend);
+    }
+
+    mainchain = pdb.getChains().elementAt(maxchain);
+
+    mainchain.pdbstart = pdbstart;
+    mainchain.pdbend = pdbend;
+    mainchain.seqstart = seqstart;
+    mainchain.seqend = seqend;
+    mainchain.isVisible = true;
+    // mainchain.makeExactMapping(maxAlignseq, sequence);
+    // mainchain.transferRESNUMFeatures(sequence, null);
+    this.pdb = pdb;
+    this.prefsize = new Dimension(getSize().width, getSize().height);
+
+    // Initialize the matrices to identity
+    for (int i = 0; i < 3; i++)
+    {
+      for (int j = 0; j < 3; j++)
+      {
+        if (i != j)
+        {
+          idmat.addElement(i, j, 0);
+          objmat.addElement(i, j, 0);
+        }
+        else
+        {
+          idmat.addElement(i, j, 1);
+          objmat.addElement(i, j, 1);
+        }
+      }
+    }
+
+    addMouseMotionListener(this);
+    addMouseListener(this);
+
+    addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        doKeyPressed(evt);
+      }
+    });
+
+    findCentre();
+    findWidth();
+
+    setupBonds();
+
+    scale = findScale();
+  }
+
+  Vector<Bond> visiblebonds;
+
+  void setupBonds()
+  {
+    seqColoursReady = false;
+    // Sort the bonds by z coord
+    visiblebonds = new Vector<Bond>();
+
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
+    {
+      if (pdb.getChains().elementAt(ii).isVisible)
+      {
+        Vector<Bond> tmp = pdb.getChains().elementAt(ii).bonds;
+
+        for (int i = 0; i < tmp.size(); i++)
+        {
+          visiblebonds.addElement(tmp.elementAt(i));
+        }
+      }
+    }
+    seqColoursReady = true;
+    colourBySequence();
+    redrawneeded = true;
+    repaint();
+  }
+
+  public void findWidth()
+  {
+    float[] max = new float[3];
+    float[] min = new float[3];
+
+    max[0] = (float) -1e30;
+    max[1] = (float) -1e30;
+    max[2] = (float) -1e30;
+
+    min[0] = (float) 1e30;
+    min[1] = (float) 1e30;
+    min[2] = (float) 1e30;
+
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
+    {
+      if (pdb.getChains().elementAt(ii).isVisible)
+      {
+        Vector<Bond> bonds = pdb.getChains().elementAt(ii).bonds;
+
+        for (Bond tmp : bonds)
+        {
+          if (tmp.start[0] >= max[0])
+          {
+            max[0] = tmp.start[0];
+          }
+
+          if (tmp.start[1] >= max[1])
+          {
+            max[1] = tmp.start[1];
+          }
+
+          if (tmp.start[2] >= max[2])
+          {
+            max[2] = tmp.start[2];
+          }
+
+          if (tmp.start[0] <= min[0])
+          {
+            min[0] = tmp.start[0];
+          }
+
+          if (tmp.start[1] <= min[1])
+          {
+            min[1] = tmp.start[1];
+          }
+
+          if (tmp.start[2] <= min[2])
+          {
+            min[2] = tmp.start[2];
+          }
+
+          if (tmp.end[0] >= max[0])
+          {
+            max[0] = tmp.end[0];
+          }
+
+          if (tmp.end[1] >= max[1])
+          {
+            max[1] = tmp.end[1];
+          }
+
+          if (tmp.end[2] >= max[2])
+          {
+            max[2] = tmp.end[2];
+          }
+
+          if (tmp.end[0] <= min[0])
+          {
+            min[0] = tmp.end[0];
+          }
+
+          if (tmp.end[1] <= min[1])
+          {
+            min[1] = tmp.end[1];
+          }
+
+          if (tmp.end[2] <= min[2])
+          {
+            min[2] = tmp.end[2];
+          }
+        }
+      }
+    }
+
+    width[0] = Math.abs(max[0] - min[0]);
+    width[1] = Math.abs(max[1] - min[1]);
+    width[2] = Math.abs(max[2] - min[2]);
+
+    maxwidth = width[0];
+
+    if (width[1] > width[0])
+    {
+      maxwidth = width[1];
+    }
+
+    if (width[2] > width[1])
+    {
+      maxwidth = width[2];
+    }
+
+    // System.out.println("Maxwidth = " + maxwidth);
+  }
+
+  public float findScale()
+  {
+    int dim;
+    int width;
+    int height;
+
+    if (getSize().width != 0)
+    {
+      width = getSize().width;
+      height = getSize().height;
+    }
+    else
+    {
+      width = prefsize.width;
+      height = prefsize.height;
+    }
+
+    if (width < height)
+    {
+      dim = width;
+    }
+    else
+    {
+      dim = height;
+    }
+
+    return (float) (dim / (1.5d * maxwidth));
+  }
+
+  public void findCentre()
+  {
+    float xtot = 0;
+    float ytot = 0;
+    float ztot = 0;
+
+    int bsize = 0;
+
+    // Find centre coordinate
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
+    {
+      if (pdb.getChains().elementAt(ii).isVisible)
+      {
+        Vector<Bond> bonds = pdb.getChains().elementAt(ii).bonds;
+
+        bsize += bonds.size();
+
+        for (Bond b : bonds)
+        {
+          xtot = xtot + b.start[0] + b.end[0];
+          ytot = ytot + b.start[1] + b.end[1];
+          ztot = ztot + b.start[2] + b.end[2];
+        }
+      }
+    }
+
+    centre[0] = xtot / (2 * (float) bsize);
+    centre[1] = ytot / (2 * (float) bsize);
+    centre[2] = ztot / (2 * (float) bsize);
+  }
+
+  @Override
+  public void paint(Graphics g)
+  {
+
+    if (errorLoading)
+    {
+      g.setColor(Color.white);
+      g.fillRect(0, 0, getSize().width, getSize().height);
+      g.setColor(Color.black);
+      g.setFont(new Font("Verdana", Font.BOLD, 14));
+      g.drawString(MessageManager.getString("label.error_loading_pdb_data"),
+              50, getSize().height / 2);
+      return;
+    }
+
+    if (!seqColoursReady)
+    {
+      g.setColor(Color.black);
+      g.setFont(new Font("Verdana", Font.BOLD, 14));
+      g.drawString(MessageManager.getString("label.fetching_pdb_data"), 50,
+              getSize().height / 2);
+      return;
+    }
+
+    // Only create the image at the beginning -
+    // this saves much memory usage
+    if ((img == null) || (prefsize.width != getSize().width)
+            || (prefsize.height != getSize().height))
+    {
+
+      try
+      {
+        prefsize.width = getSize().width;
+        prefsize.height = getSize().height;
+
+        scale = findScale();
+        img = createImage(prefsize.width, prefsize.height);
+        ig = img.getGraphics();
+
+        redrawneeded = true;
+      } catch (Exception ex)
+      {
+        ex.printStackTrace();
+      }
+    }
+
+    if (redrawneeded)
+    {
+      drawAll(ig, prefsize.width, prefsize.height);
+      redrawneeded = false;
+    }
+    if (appletToolTip != null)
+    {
+      ig.setColor(Color.red);
+      ig.drawString(appletToolTip, toolx, tooly);
+    }
+
+    g.drawImage(img, 0, 0, this);
+
+    pdbAction = false;
+  }
+
+  public void drawAll(Graphics g, int width, int height)
+  {
+    ig.setColor(Color.black);
+    ig.fillRect(0, 0, width, height);
+    drawScene(ig);
+    drawLabels(ig);
+  }
+
+  public void setColours(jalview.schemes.ColourSchemeI cs)
+  {
+    bysequence = false;
+    pdb.setColours(cs);
+    redrawneeded = true;
+    repaint();
+  }
+
+  // This method has been taken out of PDBChain to allow
+  // Applet and Application specific sequence renderers to be used
+  void colourBySequence()
+  {
+    SequenceRenderer sr = new SequenceRenderer(ap.av);
+
+    StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
+
+    boolean showFeatures = false;
+    if (ap.av.isShowSequenceFeatures())
+    {
+      if (fr == null)
+      {
+        fr = new jalview.appletgui.FeatureRenderer(ap.av);
+      }
+
+      fr.transferSettings(ap.getFeatureRenderer());
+
+      showFeatures = true;
+    }
+
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
+
+    PDBChain chain;
+    if (bysequence && pdb != null)
+    {
+      for (int ii = 0; ii < pdb.getChains().size(); ii++)
+      {
+        chain = pdb.getChains().elementAt(ii);
+
+        for (int i = 0; i < chain.bonds.size(); i++)
+        {
+          Bond tmp = chain.bonds.elementAt(i);
+          tmp.startCol = Color.lightGray;
+          tmp.endCol = Color.lightGray;
+          if (chain != mainchain)
+          {
+            continue;
+          }
+
+          for (int s = 0; s < sequence.length; s++)
+          {
+            for (int m = 0; m < mapping.length; m++)
+            {
+              if (mapping[m].getSequence() == sequence[s])
+              {
+                int pos = mapping[m].getSeqPos(tmp.at1.resNumber) - 1;
+                if (pos > 0)
+                {
+                  pos = sequence[s].findIndex(pos);
+                  tmp.startCol = sr.getResidueColour(sequence[s], pos,
+                          finder);
+                }
+                pos = mapping[m].getSeqPos(tmp.at2.resNumber) - 1;
+                if (pos > 0)
+                {
+                  pos = sequence[s].findIndex(pos);
+                  tmp.endCol = sr.getResidueColour(sequence[s], pos,
+                          finder);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  Zsort zsort;
+
+  public void drawScene(Graphics g)
+  {
+    if (zbuffer)
+    {
+      if (zsort == null)
+      {
+        zsort = new Zsort();
+      }
+
+      zsort.sort(visiblebonds);
+    }
+
+    Bond tmpBond = null;
+    for (int i = 0; i < visiblebonds.size(); i++)
+    {
+      tmpBond = visiblebonds.elementAt(i);
+
+      xstart = (int) (((tmpBond.start[0] - centre[0]) * scale)
+              + (getSize().width / 2));
+      ystart = (int) (((centre[1] - tmpBond.start[1]) * scale)
+              + (getSize().height / 2));
+
+      xend = (int) (((tmpBond.end[0] - centre[0]) * scale)
+              + (getSize().width / 2));
+      yend = (int) (((centre[1] - tmpBond.end[1]) * scale)
+              + (getSize().height / 2));
+
+      xmid = (xend + xstart) / 2;
+      ymid = (yend + ystart) / 2;
+
+      if (depthcue && !bymolecule)
+      {
+        if (tmpBond.start[2] < (centre[2] - (maxwidth / 6)))
+        {
+          g.setColor(tmpBond.startCol.darker().darker());
+          drawLine(g, xstart, ystart, xmid, ymid);
+
+          g.setColor(tmpBond.endCol.darker().darker());
+          drawLine(g, xmid, ymid, xend, yend);
+        }
+        else if (tmpBond.start[2] < (centre[2] + (maxwidth / 6)))
+        {
+          g.setColor(tmpBond.startCol.darker());
+          drawLine(g, xstart, ystart, xmid, ymid);
+
+          g.setColor(tmpBond.endCol.darker());
+          drawLine(g, xmid, ymid, xend, yend);
+        }
+        else
+        {
+          g.setColor(tmpBond.startCol);
+          drawLine(g, xstart, ystart, xmid, ymid);
+
+          g.setColor(tmpBond.endCol);
+          drawLine(g, xmid, ymid, xend, yend);
+        }
+
+      }
+      else if (depthcue && bymolecule)
+      {
+        if (tmpBond.start[2] < (centre[2] - (maxwidth / 6)))
+        {
+          g.setColor(Color.green.darker().darker());
+          drawLine(g, xstart, ystart, xend, yend);
+        }
+        else if (tmpBond.start[2] < (centre[2] + (maxwidth / 6)))
+        {
+          g.setColor(Color.green.darker());
+          drawLine(g, xstart, ystart, xend, yend);
+        }
+        else
+        {
+          g.setColor(Color.green);
+          drawLine(g, xstart, ystart, xend, yend);
+        }
+      }
+      else if (!depthcue && !bymolecule)
+      {
+        g.setColor(tmpBond.startCol);
+        drawLine(g, xstart, ystart, xmid, ymid);
+        g.setColor(tmpBond.endCol);
+        drawLine(g, xmid, ymid, xend, yend);
+      }
+      else
+      {
+        drawLine(g, xstart, ystart, xend, yend);
+      }
+
+      if (highlightBond1 != null && highlightBond1 == tmpBond)
+      {
+        g.setColor(Color.white);
+        drawLine(g, xmid, ymid, xend, yend);
+      }
+
+      if (highlightBond2 != null && highlightBond2 == tmpBond)
+      {
+        g.setColor(Color.white);
+        drawLine(g, xstart, ystart, xmid, ymid);
+      }
+
+    }
+  }
+
+  public void drawLine(Graphics g, int x1, int y1, int x2, int y2)
+  {
+    if (!wire)
+    {
+      if (((float) Math.abs(y2 - y1) / (float) Math.abs(x2 - x1)) < 0.5)
+      {
+        g.drawLine(x1, y1, x2, y2);
+        g.drawLine(x1 + 1, y1 + 1, x2 + 1, y2 + 1);
+        g.drawLine(x1, y1 - 1, x2, y2 - 1);
+      }
+      else
+      {
+        g.setColor(g.getColor().brighter());
+        g.drawLine(x1, y1, x2, y2);
+        g.drawLine(x1 + 1, y1, x2 + 1, y2);
+        g.drawLine(x1 - 1, y1, x2 - 1, y2);
+      }
+    }
+    else
+    {
+      g.drawLine(x1, y1, x2, y2);
+    }
+  }
+
+  public Dimension minimumsize()
+  {
+    return prefsize;
+  }
+
+  public Dimension preferredsize()
+  {
+    return prefsize;
+  }
+
+  public void doKeyPressed(KeyEvent evt)
+  {
+    if (evt.getKeyCode() == KeyEvent.VK_UP)
+    {
+      scale = (float) (scale * 1.1);
+      redrawneeded = true;
+      repaint();
+    }
+    else if (evt.getKeyCode() == KeyEvent.VK_DOWN)
+    {
+      scale = (float) (scale * 0.9);
+      redrawneeded = true;
+      repaint();
+    }
+  }
+
+  @Override
+  public void mousePressed(MouseEvent e)
+  {
+    pdbAction = true;
+    Atom fatom = findAtom(e.getX(), e.getY());
+    if (fatom != null)
+    {
+      fatom.isSelected = !fatom.isSelected;
+
+      redrawneeded = true;
+      repaint();
+      if (foundchain != -1)
+      {
+        PDBChain chain = pdb.getChains().elementAt(foundchain);
+        if (chain == mainchain)
+        {
+          if (fatom.alignmentMapping != -1)
+          {
+            if (highlightRes == null)
+            {
+              highlightRes = new Vector<String>();
+            }
+
+            final String atomString = Integer
+                    .toString(fatom.alignmentMapping);
+            if (highlightRes.contains(atomString))
+            {
+              highlightRes.removeElement(atomString);
+            }
+            else
+            {
+              highlightRes.addElement(atomString);
+            }
+          }
+        }
+      }
+
+    }
+    mx = e.getX();
+    my = e.getY();
+    omx = mx;
+    omy = my;
+    dragging = false;
+  }
+
+  @Override
+  public void mouseMoved(MouseEvent e)
+  {
+    pdbAction = true;
+    if (highlightBond1 != null)
+    {
+      highlightBond1.at2.isSelected = false;
+      highlightBond2.at1.isSelected = false;
+      highlightBond1 = null;
+      highlightBond2 = null;
+    }
+
+    Atom fatom = findAtom(e.getX(), e.getY());
+
+    PDBChain chain = null;
+    if (foundchain != -1)
+    {
+      chain = pdb.getChains().elementAt(foundchain);
+      if (chain == mainchain)
+      {
+        mouseOverStructure(fatom.resNumber, chain.id);
+      }
+    }
+
+    if (fatom != null)
+    {
+      toolx = e.getX();
+      tooly = e.getY();
+
+      appletToolTip = chain.id + ":" + fatom.resNumber + " "
+              + fatom.resName;
+      redrawneeded = true;
+      repaint();
+    }
+    else
+    {
+      mouseOverStructure(-1, chain != null ? chain.id : null);
+      appletToolTip = null;
+      redrawneeded = true;
+      repaint();
+    }
+  }
+
+  @Override
+  public void mouseClicked(MouseEvent e)
+  {
+  }
+
+  @Override
+  public void mouseEntered(MouseEvent e)
+  {
+  }
+
+  @Override
+  public void mouseExited(MouseEvent e)
+  {
+  }
+
+  @Override
+  public void mouseDragged(MouseEvent evt)
+  {
+    int x = evt.getX();
+    int y = evt.getY();
+    mx = x;
+    my = y;
+
+    MCMatrix objmat = new MCMatrix(3, 3);
+    objmat.setIdentity();
+
+    if ((evt.getModifiersEx() & InputEvent.META_DOWN_MASK) != 0)
+    {
+      objmat.rotatez(((mx - omx)));
+    }
+    else
+    {
+      objmat.rotatex(((omy - my)));
+      objmat.rotatey(((omx - mx)));
+    }
+
+    // Alter the bonds
+    for (PDBChain chain : pdb.getChains())
+    {
+      for (Bond tmpBond : chain.bonds)
+      {
+        // Translate the bond so the centre is 0,0,0
+        tmpBond.translate(-centre[0], -centre[1], -centre[2]);
+
+        // Now apply the rotation matrix
+        tmpBond.start = objmat.vectorMultiply(tmpBond.start);
+        tmpBond.end = objmat.vectorMultiply(tmpBond.end);
+
+        // Now translate back again
+        tmpBond.translate(centre[0], centre[1], centre[2]);
+      }
+    }
+
+    objmat = null;
+
+    omx = mx;
+    omy = my;
+
+    dragging = true;
+
+    redrawneeded = true;
+
+    repaint();
+  }
+
+  @Override
+  public void mouseReleased(MouseEvent evt)
+  {
+    dragging = false;
+    return;
+  }
+
+  void drawLabels(Graphics g)
+  {
+
+    for (PDBChain chain : pdb.getChains())
+    {
+      if (chain.isVisible)
+      {
+        for (Bond tmpBond : chain.bonds)
+        {
+          if (tmpBond.at1.isSelected)
+          {
+            labelAtom(g, tmpBond, 1);
+          }
+
+          if (tmpBond.at2.isSelected)
+          {
+            labelAtom(g, tmpBond, 2);
+          }
+        }
+      }
+    }
+  }
+
+  public void labelAtom(Graphics g, Bond b, int n)
+  {
+    g.setFont(font);
+
+    if (n == 1)
+    {
+      int xstart = (int) (((b.start[0] - centre[0]) * scale)
+              + (getSize().width / 2));
+      int ystart = (int) (((centre[1] - b.start[1]) * scale)
+              + (getSize().height / 2));
+
+      g.setColor(Color.red);
+      g.drawString(b.at1.resName + "-" + b.at1.resNumber, xstart, ystart);
+    }
+
+    if (n == 2)
+    {
+      int xstart = (int) (((b.end[0] - centre[0]) * scale)
+              + (getSize().width / 2));
+      int ystart = (int) (((centre[1] - b.end[1]) * scale)
+              + (getSize().height / 2));
+
+      g.setColor(Color.red);
+      g.drawString(b.at2.resName + "-" + b.at2.resNumber, xstart, ystart);
+    }
+  }
+
+  int foundchain = -1;
+
+  public Atom findAtom(int x, int y)
+  {
+    Atom fatom = null;
+
+    foundchain = -1;
+
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
+    {
+      PDBChain chain = pdb.getChains().elementAt(ii);
+      int truex;
+      Bond tmpBond = null;
+
+      if (chain.isVisible)
+      {
+        Vector<Bond> bonds = pdb.getChains().elementAt(ii).bonds;
+
+        for (int i = 0; i < bonds.size(); i++)
+        {
+          tmpBond = bonds.elementAt(i);
+
+          truex = (int) (((tmpBond.start[0] - centre[0]) * scale)
+                  + (getSize().width / 2));
+
+          if (Math.abs(truex - x) <= 2)
+          {
+            int truey = (int) (((centre[1] - tmpBond.start[1]) * scale)
+                    + (getSize().height / 2));
+
+            if (Math.abs(truey - y) <= 2)
+            {
+              fatom = tmpBond.at1;
+              foundchain = ii;
+              break;
+            }
+          }
+        }
+
+        // Still here? Maybe its the last bond
+
+        truex = (int) (((tmpBond.end[0] - centre[0]) * scale)
+                + (getSize().width / 2));
+
+        if (Math.abs(truex - x) <= 2)
+        {
+          int truey = (int) (((tmpBond.end[1] - centre[1]) * scale)
+                  + (getSize().height / 2));
+
+          if (Math.abs(truey - y) <= 2)
+          {
+            fatom = tmpBond.at2;
+            foundchain = ii;
+            break;
+          }
+        }
+
+      }
+
+      if (fatom != null) // )&& chain.ds != null)
+      {
+        chain = pdb.getChains().elementAt(foundchain);
+      }
+    }
+
+    return fatom;
+  }
+
+  @Override
+  public void update(Graphics g)
+  {
+    paint(g);
+  }
+
+  public void highlightRes(int ii)
+  {
+    if (!seqColoursReady)
+    {
+      return;
+    }
+
+    if (highlightRes != null && highlightRes.contains((ii - 1) + ""))
+    {
+      return;
+    }
+
+    int index = -1;
+    Bond tmpBond;
+    for (index = 0; index < mainchain.bonds.size(); index++)
+    {
+      tmpBond = mainchain.bonds.elementAt(index);
+      if (tmpBond.at1.alignmentMapping == ii - 1)
+      {
+        if (highlightBond1 != null)
+        {
+          highlightBond1.at2.isSelected = false;
+        }
+
+        if (highlightBond2 != null)
+        {
+          highlightBond2.at1.isSelected = false;
+        }
+
+        highlightBond1 = null;
+        highlightBond2 = null;
+
+        if (index > 0)
+        {
+          highlightBond1 = mainchain.bonds.elementAt(index - 1);
+          highlightBond1.at2.isSelected = true;
+        }
+
+        if (index != mainchain.bonds.size())
+        {
+          highlightBond2 = mainchain.bonds.elementAt(index);
+          highlightBond2.at1.isSelected = true;
+        }
+
+        break;
+      }
+    }
+
+    redrawneeded = true;
+    repaint();
+  }
+
+  public void setAllchainsVisible(boolean b)
+  {
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
+    {
+      PDBChain chain = pdb.getChains().elementAt(ii);
+      chain.isVisible = b;
+    }
+    mainchain.isVisible = true;
+    findCentre();
+    setupBonds();
+  }
+
+  // ////////////////////////////////
+  // /StructureListener
+  @Override
+  public String[] getStructureFiles()
+  {
+    return new String[] { pdbentry.getFile() };
+  }
+
+  String lastMessage;
+
+  public void mouseOverStructure(int pdbResNum, String chain)
+  {
+    if (lastMessage == null || !lastMessage.equals(pdbResNum + chain))
+    {
+      ssm.mouseOverStructure(pdbResNum, chain, pdbentry.getFile());
+    }
+
+    lastMessage = pdbResNum + chain;
+  }
+
+  StringBuffer resetLastRes = new StringBuffer();
+
+  StringBuffer eval = new StringBuffer();
+
+  /**
+   * Highlight the specified atoms in the structure.
+   * 
+   * @param atoms
+   */
+  @Override
+  public void highlightAtoms(List<AtomSpec> atoms)
+  {
+    if (!seqColoursReady)
+    {
+      return;
+    }
+    for (AtomSpec atom : atoms)
+    {
+      int atomIndex = atom.getAtomIndex();
+
+      if (highlightRes != null
+              && highlightRes.contains((atomIndex - 1) + ""))
+      {
+        continue;
+      }
+
+      highlightAtom(atomIndex);
+    }
+
+    redrawneeded = true;
+    repaint();
+  }
+
+  /**
+   * @param atomIndex
+   */
+  protected void highlightAtom(int atomIndex)
+  {
+    int index = -1;
+    Bond tmpBond;
+    for (index = 0; index < mainchain.bonds.size(); index++)
+    {
+      tmpBond = mainchain.bonds.elementAt(index);
+      if (tmpBond.at1.atomIndex == atomIndex)
+      {
+        if (highlightBond1 != null)
+        {
+          highlightBond1.at2.isSelected = false;
+        }
+
+        if (highlightBond2 != null)
+        {
+          highlightBond2.at1.isSelected = false;
+        }
+
+        highlightBond1 = null;
+        highlightBond2 = null;
+
+        if (index > 0)
+        {
+          highlightBond1 = mainchain.bonds.elementAt(index - 1);
+          highlightBond1.at2.isSelected = true;
+        }
+
+        if (index != mainchain.bonds.size())
+        {
+          highlightBond2 = mainchain.bonds.elementAt(index);
+          highlightBond2.at1.isSelected = true;
+        }
+
+        break;
+      }
+    }
+  }
+
+  public Color getColour(int atomIndex, int pdbResNum, String chain,
+          String pdbfile)
+  {
+    return Color.white;
+    // if (!pdbfile.equals(pdbentry.getFile()))
+    // return null;
+
+    // return new Color(viewer.getAtomArgb(atomIndex));
+  }
+
+  @Override
+  public void updateColours(Object source)
+  {
+    colourBySequence();
+    redrawneeded = true;
+    repaint();
+  }
+
+  @Override
+  public void releaseReferences(Object svl)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  @Override
+  public boolean isListeningFor(SequenceI seq)
+  {
+    if (sequence != null)
+    {
+      for (SequenceI s : sequence)
+      {
+        if (s == seq)
+        {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+}
diff --git a/src/MCview/Atom.java b/src/MCview/Atom.java
new file mode 100755 (executable)
index 0000000..a2ce2ae
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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 MCview;
+
+import jalview.schemes.ResidueProperties;
+
+import java.awt.Color;
+
+public class Atom
+{
+  public float x;
+
+  public float y;
+
+  public float z;
+
+  public int number;
+
+  public String name;
+
+  public String resName;
+
+  public int resNumber;
+
+  public char insCode = ' ';
+
+  public String resNumIns = null;
+
+  public int type;
+
+  Color color = Color.lightGray;
+
+  public String chain;
+
+  /**
+   * this is a temporary value - designed to store the position in sequence that
+   * this atom corresponds to after aligning the chain to a SequenceI object. Do
+   * not rely on its value being correct when visualizing sequence colourings on
+   * the structure - use the StructureSelectionManager's mapping instead.
+   */
+  public int alignmentMapping = -1;
+
+  public int atomIndex;
+
+  public float occupancy = 0;
+
+  public float tfactor = 0;
+
+  // need these if we ever want to export Atom data
+  // public boolean tfacset=true,occset=true;
+  public boolean isSelected = false;
+
+  public Atom(String str)
+  {
+    atomIndex = Integer.parseInt(str.substring(6, 11).trim());
+
+    name = str.substring(12, 15).trim();
+
+    resName = str.substring(17, 20);
+    // JAL-1828 treat MSE Selenomethionine as MET (etc)
+    resName = ResidueProperties.getCanonicalAminoAcid(resName);
+
+    chain = str.substring(21, 22);
+
+    resNumber = Integer.parseInt(str.substring(22, 26).trim());
+    resNumIns = str.substring(22, 27).trim();
+    insCode = str.substring(26, 27).charAt(0);
+    this.x = (Float.valueOf(str.substring(30, 38).trim()).floatValue());
+    this.y = (Float.valueOf(str.substring(38, 46).trim()).floatValue());
+    this.z = (Float.valueOf(str.substring(47, 55).trim()).floatValue());
+    // optional entries - see JAL-730
+    String tm = str.substring(54, 60).trim();
+    if (tm.length() > 0)
+    {
+      occupancy = (Float.valueOf(tm)).floatValue();
+    }
+    else
+    {
+      occupancy = 1f; // default occupancy
+      // see note above: occset=false;
+    }
+    tm = str.substring(60, 66).trim();
+    if (tm.length() > 0)
+    {
+      tfactor = (Float.valueOf(tm).floatValue());
+    }
+    else
+    {
+      tfactor = 1f;
+      // see note above: tfacset=false;
+    }
+  }
+
+  @Override
+  public boolean equals(Object that)
+  {
+    if (this == that || that == null)
+    {
+      return true;
+    }
+    if (that instanceof Atom)
+    {
+      Atom other = (Atom) that;
+      return other.resName.equals(this.resName)
+              && other.resNumber == this.resNumber
+              && other.resNumIns.equals(this.resNumIns)
+              && other.chain.equals(this.chain);
+    }
+    return false;
+  }
+
+  public Atom(float x, float y, float z)
+  {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+  }
+}
diff --git a/src/MCview/PDBCanvas.java b/src/MCview/PDBCanvas.java
new file mode 100644 (file)
index 0000000..a34e574
--- /dev/null
@@ -0,0 +1,1223 @@
+/*
+ * 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 MCview;
+
+import jalview.analysis.AlignSeq;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.gui.AlignmentPanel;
+import jalview.gui.FeatureRenderer;
+import jalview.gui.SequenceRenderer;
+import jalview.io.DataSourceType;
+import jalview.io.StructureFile;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
+import jalview.structure.AtomSpec;
+import jalview.structure.StructureListener;
+import jalview.structure.StructureMapping;
+import jalview.structure.StructureSelectionManager;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.InputEvent;import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+// JBPNote TODO: This class is quite noisy - needs proper log.info/log.debug
+import java.awt.Image;
+import java.awt.RenderingHints;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JPanel;
+import javax.swing.ToolTipManager;
+
+public class PDBCanvas extends JPanel
+        implements MouseListener, MouseMotionListener, StructureListener
+{
+  boolean redrawneeded = true;
+
+  int omx = 0;
+
+  int mx = 0;
+
+  int omy = 0;
+
+  int my = 0;
+
+  public StructureFile pdb;
+
+  PDBEntry pdbentry;
+
+  int bsize;
+
+  Image img;
+
+  Graphics ig;
+
+  Dimension prefsize;
+
+  float[] centre = new float[3];
+
+  float[] width = new float[3];
+
+  float maxwidth;
+
+  float scale;
+
+  String inStr;
+
+  String inType;
+
+  boolean bysequence = true;
+
+  boolean depthcue = true;
+
+  boolean wire = false;
+
+  boolean bymolecule = false;
+
+  boolean zbuffer = true;
+
+  boolean dragging;
+
+  int xstart;
+
+  int xend;
+
+  int ystart;
+
+  int yend;
+
+  int xmid;
+
+  int ymid;
+
+  Font font = new Font("Helvetica", Font.PLAIN, 10);
+
+  jalview.gui.SeqCanvas seqcanvas;
+
+  public SequenceI[] sequence;
+
+  final StringBuffer mappingDetails = new StringBuffer();
+
+  PDBChain mainchain;
+
+  Vector<String> highlightRes;
+
+  boolean pdbAction = false;
+
+  boolean seqColoursReady = false;
+
+  jalview.renderer.seqfeatures.FeatureRenderer fr;
+
+  Color backgroundColour = Color.black;
+
+  AlignmentPanel ap;
+
+  StructureSelectionManager ssm;
+
+  String errorMessage;
+
+  void init(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
+          AlignmentPanel ap, DataSourceType protocol)
+  {
+    this.ap = ap;
+    this.pdbentry = pdbentry;
+    this.sequence = seq;
+
+    ssm = ap.av.getStructureSelectionManager();
+
+    try
+    {
+      pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol,
+              ap.alignFrame);
+
+      if (protocol.equals(jalview.io.DataSourceType.PASTE))
+      {
+        pdbentry.setFile("INLINE" + pdb.getId());
+      }
+
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
+      return;
+    }
+
+    if (pdb == null)
+    {
+      errorMessage = "Error loading file: " + pdbentry.getId();
+      return;
+    }
+    pdbentry.setId(pdb.getId());
+
+    ssm.addStructureViewerListener(this);
+
+    colourBySequence();
+
+    float max = -10;
+    int maxchain = -1;
+    int pdbstart = 0;
+    int pdbend = 0;
+    int seqstart = 0;
+    int seqend = 0;
+
+    // JUST DEAL WITH ONE SEQUENCE FOR NOW
+    SequenceI sequence = seq[0];
+
+    for (int i = 0; i < pdb.getChains().size(); i++)
+    {
+
+      mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
+              + pdb.getChains().elementAt(i).sequence
+                      .getSequenceAsString());
+      mappingDetails.append("\nNo of residues = "
+              + pdb.getChains().elementAt(i).residues.size() + "\n\n");
+
+      // Now lets compare the sequences to get
+      // the start and end points.
+      // Align the sequence to the pdb
+      AlignSeq as = new AlignSeq(sequence,
+              pdb.getChains().elementAt(i).sequence, "pep");
+      as.calcScoreMatrix();
+      as.traceAlignment();
+      PrintStream ps = new PrintStream(System.out)
+      {
+        @Override
+        public void print(String x)
+        {
+          mappingDetails.append(x);
+        }
+
+        @Override
+        public void println()
+        {
+          mappingDetails.append("\n");
+        }
+      };
+
+      as.printAlignment(ps);
+
+      if (as.maxscore > max)
+      {
+        max = as.maxscore;
+        maxchain = i;
+
+        pdbstart = as.seq2start;
+        pdbend = as.seq2end;
+        seqstart = as.seq1start + sequence.getStart() - 1;
+        seqend = as.seq1end + sequence.getEnd() - 1;
+      }
+
+      mappingDetails.append("\nPDB start/end " + pdbstart + " " + pdbend);
+      mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend);
+    }
+
+    mainchain = pdb.getChains().elementAt(maxchain);
+
+    mainchain.pdbstart = pdbstart;
+    mainchain.pdbend = pdbend;
+    mainchain.seqstart = seqstart;
+    mainchain.seqend = seqend;
+    mainchain.isVisible = true;
+
+    this.pdb = pdb;
+    this.prefsize = new Dimension(getSize().width, getSize().height);
+
+    addMouseMotionListener(this);
+    addMouseListener(this);
+
+    addKeyListener(new KeyAdapter()
+    {
+      @Override
+      public void keyPressed(KeyEvent evt)
+      {
+        keyPressed(evt);
+      }
+    });
+
+    findCentre();
+    findWidth();
+
+    setupBonds();
+
+    scale = findScale();
+
+    ToolTipManager.sharedInstance().registerComponent(this);
+    ToolTipManager.sharedInstance().setInitialDelay(0);
+    ToolTipManager.sharedInstance().setDismissDelay(10000);
+  }
+
+  Vector<Bond> visiblebonds;
+
+  void setupBonds()
+  {
+    seqColoursReady = false;
+    // Sort the bonds by z coord
+    visiblebonds = new Vector<Bond>();
+
+    for (PDBChain chain : pdb.getChains())
+    {
+      if (chain.isVisible)
+      {
+        for (Bond bond : chain.bonds)
+        {
+          visiblebonds.addElement(bond);
+        }
+      }
+    }
+
+    updateSeqColours();
+    seqColoursReady = true;
+    redrawneeded = true;
+    repaint();
+  }
+
+  public void findWidth()
+  {
+    float[] max = new float[3];
+    float[] min = new float[3];
+
+    max[0] = (float) -1e30;
+    max[1] = (float) -1e30;
+    max[2] = (float) -1e30;
+
+    min[0] = (float) 1e30;
+    min[1] = (float) 1e30;
+    min[2] = (float) 1e30;
+
+    for (PDBChain chain : pdb.getChains())
+    {
+      if (chain.isVisible)
+      {
+        for (Bond tmp : chain.bonds)
+        {
+          if (tmp.start[0] >= max[0])
+          {
+            max[0] = tmp.start[0];
+          }
+
+          if (tmp.start[1] >= max[1])
+          {
+            max[1] = tmp.start[1];
+          }
+
+          if (tmp.start[2] >= max[2])
+          {
+            max[2] = tmp.start[2];
+          }
+
+          if (tmp.start[0] <= min[0])
+          {
+            min[0] = tmp.start[0];
+          }
+
+          if (tmp.start[1] <= min[1])
+          {
+            min[1] = tmp.start[1];
+          }
+
+          if (tmp.start[2] <= min[2])
+          {
+            min[2] = tmp.start[2];
+          }
+
+          if (tmp.end[0] >= max[0])
+          {
+            max[0] = tmp.end[0];
+          }
+
+          if (tmp.end[1] >= max[1])
+          {
+            max[1] = tmp.end[1];
+          }
+
+          if (tmp.end[2] >= max[2])
+          {
+            max[2] = tmp.end[2];
+          }
+
+          if (tmp.end[0] <= min[0])
+          {
+            min[0] = tmp.end[0];
+          }
+
+          if (tmp.end[1] <= min[1])
+          {
+            min[1] = tmp.end[1];
+          }
+
+          if (tmp.end[2] <= min[2])
+          {
+            min[2] = tmp.end[2];
+          }
+        }
+      }
+    }
+    /*
+     * System.out.println("xmax " + max[0] + " min " + min[0]);
+     * System.out.println("ymax " + max[1] + " min " + min[1]);
+     * System.out.println("zmax " + max[2] + " min " + min[2]);
+     */
+
+    width[0] = Math.abs(max[0] - min[0]);
+    width[1] = Math.abs(max[1] - min[1]);
+    width[2] = Math.abs(max[2] - min[2]);
+
+    maxwidth = width[0];
+
+    if (width[1] > width[0])
+    {
+      maxwidth = width[1];
+    }
+
+    if (width[2] > width[1])
+    {
+      maxwidth = width[2];
+    }
+
+    // System.out.println("Maxwidth = " + maxwidth);
+  }
+
+  public float findScale()
+  {
+    int dim;
+    int width;
+    int height;
+
+    if (getWidth() != 0)
+    {
+      width = getWidth();
+      height = getHeight();
+    }
+    else
+    {
+      width = prefsize.width;
+      height = prefsize.height;
+    }
+
+    if (width < height)
+    {
+      dim = width;
+    }
+    else
+    {
+      dim = height;
+    }
+
+    return (float) (dim / (1.5d * maxwidth));
+  }
+
+  public void findCentre()
+  {
+    float xtot = 0;
+    float ytot = 0;
+    float ztot = 0;
+
+    int bsize = 0;
+
+    // Find centre coordinate
+    for (PDBChain chain : pdb.getChains())
+    {
+      if (chain.isVisible)
+      {
+        bsize += chain.bonds.size();
+
+        for (Bond bond : chain.bonds)
+        {
+          xtot = xtot + bond.start[0] + bond.end[0];
+          ytot = ytot + bond.start[1] + bond.end[1];
+          ztot = ztot + bond.start[2] + bond.end[2];
+        }
+      }
+    }
+
+    centre[0] = xtot / (2 * (float) bsize);
+    centre[1] = ytot / (2 * (float) bsize);
+    centre[2] = ztot / (2 * (float) bsize);
+  }
+
+  @Override
+  public void paintComponent(Graphics g)
+  {
+    super.paintComponent(g);
+
+    if (!seqColoursReady || errorMessage != null)
+    {
+      g.setColor(Color.black);
+      g.setFont(new Font("Verdana", Font.BOLD, 14));
+      g.drawString(errorMessage == null ? "Retrieving PDB data...."
+              : errorMessage, 20, getHeight() / 2);
+      return;
+    }
+
+    // Only create the image at the beginning -
+    // this saves much memory usage
+    if ((img == null) || (prefsize.width != getWidth())
+            || (prefsize.height != getHeight()))
+
+    {
+      prefsize.width = getWidth();
+      prefsize.height = getHeight();
+
+      scale = findScale();
+      img = createImage(prefsize.width, prefsize.height);
+      ig = img.getGraphics();
+      Graphics2D ig2 = (Graphics2D) ig;
+
+      ig2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+              RenderingHints.VALUE_ANTIALIAS_ON);
+
+      redrawneeded = true;
+    }
+
+    if (redrawneeded)
+    {
+      drawAll(ig, prefsize.width, prefsize.height);
+      redrawneeded = false;
+    }
+
+    g.drawImage(img, 0, 0, this);
+
+    pdbAction = false;
+  }
+
+  public void drawAll(Graphics g, int width, int height)
+  {
+    g.setColor(backgroundColour);
+    g.fillRect(0, 0, width, height);
+    drawScene(g);
+    drawLabels(g);
+  }
+
+  public void updateSeqColours()
+  {
+    if (pdbAction)
+    {
+      return;
+    }
+
+    colourBySequence();
+
+    redrawneeded = true;
+    repaint();
+  }
+
+  // This method has been taken out of PDBChain to allow
+  // Applet and Application specific sequence renderers to be used
+  void colourBySequence()
+  {
+    SequenceRenderer sr = new SequenceRenderer(ap.av);
+
+    StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
+
+    boolean showFeatures = false;
+    if (ap.av.isShowSequenceFeatures())
+    {
+      if (fr == null)
+      {
+        fr = new FeatureRenderer(ap);
+      }
+
+      fr.transferSettings(ap.alignFrame.getFeatureRenderer());
+
+      showFeatures = true;
+    }
+
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
+    PDBChain chain;
+    if (bysequence && pdb != null)
+    {
+      for (int ii = 0; ii < pdb.getChains().size(); ii++)
+      {
+        chain = pdb.getChains().elementAt(ii);
+
+        for (int i = 0; i < chain.bonds.size(); i++)
+        {
+          Bond tmp = chain.bonds.elementAt(i);
+          tmp.startCol = Color.lightGray;
+          tmp.endCol = Color.lightGray;
+          if (chain != mainchain)
+          {
+            continue;
+          }
+
+          for (int s = 0; s < sequence.length; s++)
+          {
+            for (int m = 0; m < mapping.length; m++)
+            {
+              if (mapping[m].getSequence() == sequence[s])
+              {
+                int pos = mapping[m].getSeqPos(tmp.at1.resNumber) - 1;
+                if (pos > 0)
+                {
+                  pos = sequence[s].findIndex(pos);
+                  tmp.startCol = sr.getResidueColour(sequence[s], pos,
+                          finder);
+                }
+                pos = mapping[m].getSeqPos(tmp.at2.resNumber) - 1;
+                if (pos > 0)
+                {
+                  pos = sequence[s].findIndex(pos);
+                  tmp.endCol = sr.getResidueColour(sequence[s], pos,
+                          finder);
+                }
+
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  Zsort zsort;
+
+  public void drawScene(Graphics g)
+  {
+    if (zbuffer)
+    {
+      if (zsort == null)
+      {
+        zsort = new Zsort();
+      }
+
+      zsort.sort(visiblebonds);
+    }
+
+    Bond tmpBond = null;
+    for (int i = 0; i < visiblebonds.size(); i++)
+    {
+      tmpBond = visiblebonds.elementAt(i);
+
+      xstart = (int) (((tmpBond.start[0] - centre[0]) * scale)
+              + (getWidth() / 2));
+      ystart = (int) (((centre[1] - tmpBond.start[1]) * scale)
+              + (getHeight() / 2));
+
+      xend = (int) (((tmpBond.end[0] - centre[0]) * scale)
+              + (getWidth() / 2));
+      yend = (int) (((centre[1] - tmpBond.end[1]) * scale)
+              + (getHeight() / 2));
+
+      xmid = (xend + xstart) / 2;
+      ymid = (yend + ystart) / 2;
+      if (depthcue && !bymolecule)
+      {
+        if (tmpBond.start[2] < (centre[2] - (maxwidth / 6)))
+        {
+
+          g.setColor(tmpBond.startCol.darker().darker());
+          drawLine(g, xstart, ystart, xmid, ymid);
+          g.setColor(tmpBond.endCol.darker().darker());
+          drawLine(g, xmid, ymid, xend, yend);
+
+        }
+        else if (tmpBond.start[2] < (centre[2] + (maxwidth / 6)))
+        {
+          g.setColor(tmpBond.startCol.darker());
+          drawLine(g, xstart, ystart, xmid, ymid);
+
+          g.setColor(tmpBond.endCol.darker());
+          drawLine(g, xmid, ymid, xend, yend);
+        }
+        else
+        {
+          g.setColor(tmpBond.startCol);
+          drawLine(g, xstart, ystart, xmid, ymid);
+
+          g.setColor(tmpBond.endCol);
+          drawLine(g, xmid, ymid, xend, yend);
+        }
+      }
+      else if (depthcue && bymolecule)
+      {
+        if (tmpBond.start[2] < (centre[2] - (maxwidth / 6)))
+        {
+          g.setColor(Color.green.darker().darker());
+          drawLine(g, xstart, ystart, xend, yend);
+        }
+        else if (tmpBond.start[2] < (centre[2] + (maxwidth / 6)))
+        {
+          g.setColor(Color.green.darker());
+          drawLine(g, xstart, ystart, xend, yend);
+        }
+        else
+        {
+          g.setColor(Color.green);
+          drawLine(g, xstart, ystart, xend, yend);
+        }
+      }
+      else if (!depthcue && !bymolecule)
+      {
+        g.setColor(tmpBond.startCol);
+        drawLine(g, xstart, ystart, xmid, ymid);
+        g.setColor(tmpBond.endCol);
+        drawLine(g, xmid, ymid, xend, yend);
+      }
+      else
+      {
+        drawLine(g, xstart, ystart, xend, yend);
+      }
+
+      if (highlightBond1 != null && highlightBond1 == tmpBond)
+      {
+        g.setColor(
+                tmpBond.endCol.brighter().brighter().brighter().brighter());
+        drawLine(g, xmid, ymid, xend, yend);
+      }
+
+      if (highlightBond2 != null && highlightBond2 == tmpBond)
+      {
+        g.setColor(tmpBond.startCol.brighter().brighter().brighter()
+                .brighter());
+        drawLine(g, xstart, ystart, xmid, ymid);
+      }
+
+    }
+
+  }
+
+  public void drawLine(Graphics g, int x1, int y1, int x2, int y2)
+  {
+    if (!wire)
+    {
+      if (((float) Math.abs(y2 - y1) / (float) Math.abs(x2 - x1)) < 0.5)
+      {
+        g.drawLine(x1, y1, x2, y2);
+        g.drawLine(x1 + 1, y1 + 1, x2 + 1, y2 + 1);
+        g.drawLine(x1, y1 - 1, x2, y2 - 1);
+      }
+      else
+      {
+        g.setColor(g.getColor().brighter());
+        g.drawLine(x1, y1, x2, y2);
+        g.drawLine(x1 + 1, y1, x2 + 1, y2);
+        g.drawLine(x1 - 1, y1, x2 - 1, y2);
+      }
+    }
+    else
+    {
+      g.drawLine(x1, y1, x2, y2);
+    }
+  }
+
+  public Dimension minimumsize()
+  {
+    return prefsize;
+  }
+
+  public Dimension preferredsize()
+  {
+    return prefsize;
+  }
+
+  public void keyPressed(KeyEvent evt)
+  {
+    if (evt.getKeyCode() == KeyEvent.VK_UP)
+    {
+      scale = (float) (scale * 1.1);
+      redrawneeded = true;
+      repaint();
+    }
+    else if (evt.getKeyCode() == KeyEvent.VK_DOWN)
+    {
+      scale = (float) (scale * 0.9);
+      redrawneeded = true;
+      repaint();
+    }
+  }
+
+  @Override
+  public void mousePressed(MouseEvent e)
+  {
+    pdbAction = true;
+    Atom fatom = findAtom(e.getX(), e.getY());
+    if (fatom != null)
+    {
+      fatom.isSelected = !fatom.isSelected;
+
+      redrawneeded = true;
+      repaint();
+      if (foundchain != -1)
+      {
+        PDBChain chain = pdb.getChains().elementAt(foundchain);
+        if (chain == mainchain)
+        {
+          if (fatom.alignmentMapping != -1)
+          {
+            if (highlightRes == null)
+            {
+              highlightRes = new Vector<String>();
+            }
+
+            final String atomString = Integer
+                    .toString(fatom.alignmentMapping);
+            if (highlightRes.contains(atomString))
+            {
+              highlightRes.remove(atomString);
+            }
+            else
+            {
+              highlightRes.add(atomString);
+            }
+          }
+        }
+      }
+
+    }
+    mx = e.getX();
+    my = e.getY();
+    omx = mx;
+    omy = my;
+    dragging = false;
+  }
+
+  @Override
+  public void mouseMoved(MouseEvent e)
+  {
+    pdbAction = true;
+    if (highlightBond1 != null)
+    {
+      highlightBond1.at2.isSelected = false;
+      highlightBond2.at1.isSelected = false;
+      highlightBond1 = null;
+      highlightBond2 = null;
+    }
+
+    Atom fatom = findAtom(e.getX(), e.getY());
+
+    PDBChain chain = null;
+    if (foundchain != -1)
+    {
+      chain = pdb.getChains().elementAt(foundchain);
+      if (chain == mainchain)
+      {
+        mouseOverStructure(fatom.resNumber, chain.id);
+      }
+    }
+
+    if (fatom != null)
+    {
+      this.setToolTipText(
+              chain.id + ":" + fatom.resNumber + " " + fatom.resName);
+    }
+    else
+    {
+      mouseOverStructure(-1, chain != null ? chain.id : null);
+      this.setToolTipText(null);
+    }
+  }
+
+  @Override
+  public void mouseClicked(MouseEvent e)
+  {
+  }
+
+  @Override
+  public void mouseEntered(MouseEvent e)
+  {
+  }
+
+  @Override
+  public void mouseExited(MouseEvent e)
+  {
+  }
+
+  @Override
+  public void mouseDragged(MouseEvent evt)
+  {
+    int x = evt.getX();
+    int y = evt.getY();
+    mx = x;
+    my = y;
+
+    MCMatrix objmat = new MCMatrix(3, 3);
+    objmat.setIdentity();
+
+    if ((evt.getModifiersEx() & InputEvent.META_DOWN_MASK) != 0)
+    {
+      objmat.rotatez(((mx - omx)));
+    }
+    else
+    {
+      objmat.rotatex(((my - omy)));
+      objmat.rotatey(((omx - mx)));
+    }
+
+    // Alter the bonds
+    for (PDBChain chain : pdb.getChains())
+    {
+      for (Bond tmpBond : chain.bonds)
+      {
+        // Translate the bond so the centre is 0,0,0
+        tmpBond.translate(-centre[0], -centre[1], -centre[2]);
+
+        // Now apply the rotation matrix
+        tmpBond.start = objmat.vectorMultiply(tmpBond.start);
+        tmpBond.end = objmat.vectorMultiply(tmpBond.end);
+
+        // Now translate back again
+        tmpBond.translate(centre[0], centre[1], centre[2]);
+      }
+    }
+
+    objmat = null;
+
+    omx = mx;
+    omy = my;
+
+    dragging = true;
+
+    redrawneeded = true;
+
+    repaint();
+  }
+
+  @Override
+  public void mouseReleased(MouseEvent evt)
+  {
+    dragging = false;
+    return;
+  }
+
+  void drawLabels(Graphics g)
+  {
+
+    for (PDBChain chain : pdb.getChains())
+    {
+      if (chain.isVisible)
+      {
+        for (Bond tmpBond : chain.bonds)
+        {
+          if (tmpBond.at1.isSelected)
+          {
+            labelAtom(g, tmpBond, 1);
+          }
+
+          if (tmpBond.at2.isSelected)
+          {
+            labelAtom(g, tmpBond, 2);
+          }
+        }
+      }
+    }
+  }
+
+  public void labelAtom(Graphics g, Bond b, int n)
+  {
+    g.setFont(font);
+    g.setColor(Color.red);
+    if (n == 1)
+    {
+      int xstart = (int) (((b.start[0] - centre[0]) * scale)
+              + (getWidth() / 2));
+      int ystart = (int) (((centre[1] - b.start[1]) * scale)
+              + (getHeight() / 2));
+
+      g.drawString(b.at1.resName + "-" + b.at1.resNumber, xstart, ystart);
+    }
+
+    if (n == 2)
+    {
+      int xstart = (int) (((b.end[0] - centre[0]) * scale)
+              + (getWidth() / 2));
+      int ystart = (int) (((centre[1] - b.end[1]) * scale)
+              + (getHeight() / 2));
+
+      g.drawString(b.at2.resName + "-" + b.at2.resNumber, xstart, ystart);
+    }
+  }
+
+  int foundchain = -1;
+
+  public Atom findAtom(int x, int y)
+  {
+    Atom fatom = null;
+
+    foundchain = -1;
+
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
+    {
+      PDBChain chain = pdb.getChains().elementAt(ii);
+      int truex;
+      Bond tmpBond = null;
+
+      if (chain.isVisible)
+      {
+        for (Bond bond : chain.bonds)
+        {
+          tmpBond = bond;
+
+          truex = (int) (((tmpBond.start[0] - centre[0]) * scale)
+                  + (getWidth() / 2));
+
+          if (Math.abs(truex - x) <= 2)
+          {
+            int truey = (int) (((centre[1] - tmpBond.start[1]) * scale)
+                    + (getHeight() / 2));
+
+            if (Math.abs(truey - y) <= 2)
+            {
+              fatom = tmpBond.at1;
+              foundchain = ii;
+              break;
+            }
+          }
+        }
+
+        // Still here? Maybe its the last bond
+
+        truex = (int) (((tmpBond.end[0] - centre[0]) * scale)
+                + (getWidth() / 2));
+
+        if (Math.abs(truex - x) <= 2)
+        {
+          int truey = (int) (((tmpBond.end[1] - centre[1]) * scale)
+                  + (getHeight() / 2));
+
+          if (Math.abs(truey - y) <= 2)
+          {
+            fatom = tmpBond.at2;
+            foundchain = ii;
+            break;
+          }
+        }
+
+      }
+
+      if (fatom != null) // )&& chain.ds != null)
+      { // dead code? value of chain is either overwritten or discarded
+        chain = pdb.getChains().elementAt(foundchain);
+      }
+    }
+
+    return fatom;
+  }
+
+  Bond highlightBond1, highlightBond2;
+
+  public void highlightRes(int ii)
+  {
+    if (!seqColoursReady)
+    {
+      return;
+    }
+
+    if (highlightRes != null && highlightRes.contains((ii - 1) + ""))
+    {
+      return;
+    }
+
+    int index = -1;
+    Bond tmpBond;
+    for (index = 0; index < mainchain.bonds.size(); index++)
+    {
+      tmpBond = mainchain.bonds.elementAt(index);
+      if (tmpBond.at1.alignmentMapping == ii - 1)
+      {
+        if (highlightBond1 != null)
+        {
+          highlightBond1.at2.isSelected = false;
+        }
+
+        if (highlightBond2 != null)
+        {
+          highlightBond2.at1.isSelected = false;
+        }
+
+        highlightBond1 = null;
+        highlightBond2 = null;
+
+        if (index > 0)
+        {
+          highlightBond1 = mainchain.bonds.elementAt(index - 1);
+          highlightBond1.at2.isSelected = true;
+        }
+
+        if (index != mainchain.bonds.size())
+        {
+          highlightBond2 = mainchain.bonds.elementAt(index);
+          highlightBond2.at1.isSelected = true;
+        }
+
+        break;
+      }
+    }
+
+    redrawneeded = true;
+    repaint();
+  }
+
+  public void setAllchainsVisible(boolean b)
+  {
+    for (int ii = 0; ii < pdb.getChains().size(); ii++)
+    {
+      PDBChain chain = pdb.getChains().elementAt(ii);
+      chain.isVisible = b;
+    }
+    mainchain.isVisible = true;
+    findCentre();
+    setupBonds();
+  }
+
+  // ////////////////////////////////
+  // /StructureListener
+  @Override
+  public String[] getStructureFiles()
+  {
+    return new String[] { pdbentry.getFile() };
+  }
+
+  String lastMessage;
+
+  public void mouseOverStructure(int pdbResNum, String chain)
+  {
+    if (lastMessage == null || !lastMessage.equals(pdbResNum + chain))
+    {
+      ssm.mouseOverStructure(pdbResNum, chain, pdbentry.getFile());
+    }
+
+    lastMessage = pdbResNum + chain;
+  }
+
+  StringBuffer resetLastRes = new StringBuffer();
+
+  StringBuffer eval = new StringBuffer();
+
+  /**
+   * Highlight the specified atoms in the structure.
+   * 
+   * @param atoms
+   */
+  @Override
+  public void highlightAtoms(List<AtomSpec> atoms)
+  {
+    if (!seqColoursReady)
+    {
+      return;
+    }
+
+    for (AtomSpec atom : atoms)
+    {
+      int atomIndex = atom.getAtomIndex();
+      if (highlightRes != null
+              && highlightRes.contains((atomIndex - 1) + ""))
+      {
+        continue;
+      }
+
+      highlightAtom(atomIndex);
+    }
+
+    redrawneeded = true;
+    repaint();
+  }
+
+  /**
+   * Highlight the atom at the specified index.
+   * 
+   * @param atomIndex
+   */
+  protected void highlightAtom(int atomIndex)
+  {
+    int index = -1;
+    Bond tmpBond;
+    for (index = 0; index < mainchain.bonds.size(); index++)
+    {
+      tmpBond = mainchain.bonds.elementAt(index);
+      if (tmpBond.at1.atomIndex == atomIndex)
+      {
+        if (highlightBond1 != null)
+        {
+          highlightBond1.at2.isSelected = false;
+        }
+
+        if (highlightBond2 != null)
+        {
+          highlightBond2.at1.isSelected = false;
+        }
+
+        highlightBond1 = null;
+        highlightBond2 = null;
+
+        if (index > 0)
+        {
+          highlightBond1 = mainchain.bonds.elementAt(index - 1);
+          highlightBond1.at2.isSelected = true;
+        }
+
+        if (index != mainchain.bonds.size())
+        {
+          highlightBond2 = mainchain.bonds.elementAt(index);
+          highlightBond2.at1.isSelected = true;
+        }
+
+        break;
+      }
+    }
+  }
+
+  public Color getColour(int atomIndex, int pdbResNum, String chain,
+          String pdbfile)
+  {
+    return Color.white;
+    // if (!pdbfile.equals(pdbentry.getFile()))
+    // return null;
+
+    // return new Color(viewer.getAtomArgb(atomIndex));
+  }
+
+  @Override
+  public void updateColours(Object source)
+  {
+    colourBySequence();
+    redrawneeded = true;
+    repaint();
+  }
+
+  @Override
+  public void releaseReferences(Object svl)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  @Override
+  public boolean isListeningFor(SequenceI seq)
+  {
+    if (sequence != null)
+    {
+      for (SequenceI s : sequence)
+      {
+        if (s == seq)
+        {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+}
index 50d799f..d5f62de 100755 (executable)
@@ -138,16 +138,16 @@ class Branch extends Pattern
       n = RegOpt.opt(n, ignoreCase, dontMinQ);
     }
     n.setParent(this);
-    set(new Character(o.c), n, ignoreCase, dontMinQ);
+    set(Character.valueOf(o.c), n, ignoreCase, dontMinQ);
     if (ignoreCase)
     {
       if (o.c != o.altc)
       {
-        set(new Character(o.altc), n, ignoreCase, dontMinQ);
+        set(Character.valueOf(o.altc), n, ignoreCase, dontMinQ);
       }
       if (o.c != o.altc2 && o.altc != o.altc2)
       {
-        set(new Character(o.altc2), n, ignoreCase, dontMinQ);
+        set(Character.valueOf(o.altc2), n, ignoreCase, dontMinQ);
       }
     }
   }
@@ -250,7 +250,7 @@ class Branch extends Pattern
     {
       return -1;
     }
-    Pattern n = (Pattern) h.get(new Character(pt.src.charAt(pos)));
+    Pattern n = (Pattern) h.get(Character.valueOf(pt.src.charAt(pos)));
     if (n == null)
     {
       return -1;
index c00ddad..6d07427 100755 (executable)
@@ -19,6 +19,7 @@ import com.stevesoft.pat.wrap.StringWrap;
 /** Matches a Unicode punctuation character. */
 class UnicodePunct extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && Prop.isPunct(s.charAt(from)) ? to : -1;
@@ -28,6 +29,7 @@ class UnicodePunct extends UniValidator
 /** Matches a Unicode white space character. */
 class UnicodeWhite extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && Prop.isWhite(s.charAt(from)) ? to : -1;
@@ -39,6 +41,7 @@ class UnicodeWhite extends UniValidator
  */
 class NUnicodePunct extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && !Prop.isPunct(s.charAt(from)) ? to : -1;
@@ -50,6 +53,7 @@ class NUnicodePunct extends UniValidator
  */
 class NUnicodeWhite extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && !Prop.isWhite(s.charAt(from)) ? to : -1;
@@ -59,6 +63,7 @@ class NUnicodeWhite extends UniValidator
 /** Matches a Unicode word character: an alphanumeric or underscore. */
 class UnicodeW extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     if (from >= s.length())
@@ -74,6 +79,7 @@ class UnicodeW extends UniValidator
 /** Matches a character that is not a Unicode alphanumeric or underscore. */
 class NUnicodeW extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     if (from >= s.length())
@@ -89,6 +95,7 @@ class NUnicodeW extends UniValidator
 /** Matches a Unicode decimal digit. */
 class UnicodeDigit extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && Prop.isDecimalDigit(s.charAt(from)) ? to
@@ -99,6 +106,7 @@ class UnicodeDigit extends UniValidator
 /** Matches a character that is not a Unicode digit. */
 class NUnicodeDigit extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && !Prop.isDecimalDigit(s.charAt(from)) ? to
@@ -109,6 +117,7 @@ class NUnicodeDigit extends UniValidator
 /** Matches a Unicode math character. */
 class UnicodeMath extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && Prop.isMath(s.charAt(from)) ? to : -1;
@@ -118,6 +127,7 @@ class UnicodeMath extends UniValidator
 /** Matches a non-math Unicode character. */
 class NUnicodeMath extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && !Prop.isMath(s.charAt(from)) ? to : -1;
@@ -127,6 +137,7 @@ class NUnicodeMath extends UniValidator
 /** Matches a Unicode currency symbol. */
 class UnicodeCurrency extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && Prop.isCurrency(s.charAt(from)) ? to : -1;
@@ -136,6 +147,7 @@ class UnicodeCurrency extends UniValidator
 /** Matches a non-currency symbol Unicode character. */
 class NUnicodeCurrency extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && !Prop.isCurrency(s.charAt(from)) ? to : -1;
@@ -145,6 +157,7 @@ class NUnicodeCurrency extends UniValidator
 /** Matches a Unicode alphabetic character. */
 class UnicodeAlpha extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && Prop.isAlphabetic(s.charAt(from)) ? to : -1;
@@ -154,6 +167,7 @@ class UnicodeAlpha extends UniValidator
 /** Matches a non-alphabetic Unicode character. */
 class NUnicodeAlpha extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && !Prop.isAlphabetic(s.charAt(from)) ? to
@@ -164,6 +178,7 @@ class NUnicodeAlpha extends UniValidator
 /** Matches an upper case Unicode character. */
 class UnicodeUpper extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && isUpper(s.charAt(from)) ? to : -1;
@@ -178,6 +193,7 @@ class UnicodeUpper extends UniValidator
 /** Matches an upper case Unicode character. */
 class UnicodeLower extends UniValidator
 {
+  @Override
   public int validate(StringLike s, int from, int to)
   {
     return from < s.length() && isLower(s.charAt(from)) ? to : -1;
@@ -599,7 +615,7 @@ public class Regex extends RegRes implements FilenameFilter
   /** Essentially clones the Regex object */
   public Regex(Regex r)
   {
-    super((RegRes) r);
+    super(r);
     dontMatchInQuotes = r.dontMatchInQuotes;
     esc = r.esc;
     ignoreCase = r.ignoreCase;
@@ -674,6 +690,7 @@ public class Regex extends RegRes implements FilenameFilter
    * patterns are equal as well as the most recent match. If a Regex is compare
    * with a RegRes, only the result of the most recent match is compared.
    */
+  @Override
   public boolean equals(Object o)
   {
     if (o instanceof Regex)
@@ -694,6 +711,7 @@ public class Regex extends RegRes implements FilenameFilter
   }
 
   /** A clone by any other name would smell as sweet. */
+  @Override
   public Object clone()
   {
     return new Regex(this);
@@ -1077,13 +1095,16 @@ public class Regex extends RegRes implements FilenameFilter
   {
     try
     {
-      return (Regex) getClass().newInstance();
+      return getClass().getDeclaredConstructor().newInstance();
     } catch (InstantiationException ie)
     {
       return null;
     } catch (IllegalAccessException iae)
     {
       return null;
+    } catch (ReflectiveOperationException roe)
+    {
+      return null;
     }
   }
 
@@ -1641,7 +1662,7 @@ public class Regex extends RegRes implements FilenameFilter
   {
     if (p instanceof Any && p.next == null)
     {
-      return (Pattern) new DotMulti(lo, hi);
+      return new DotMulti(lo, hi);
     }
     return RegOpt.safe4fm(p) ? (Pattern) new FastMulti(lo, hi, p)
             : (Pattern) new Multi(lo, hi, p);
@@ -1801,6 +1822,7 @@ public class Regex extends RegRes implements FilenameFilter
    * representations. Also be prepared to see some strange output if your
    * characters are not printable.
    */
+  @Override
   public String toString()
   {
     if (false && thePattern == null)
@@ -1901,6 +1923,7 @@ public class Regex extends RegRes implements FilenameFilter
    * 
    * @see com.stevesoft.pat.FileRegex
    */
+  @Override
   public boolean accept(File dir, String s)
   {
     return search(s);
index 837821e..a0b42ce 100755 (executable)
@@ -233,6 +233,7 @@ public class RegexReader extends Reader
    * 
    * @deprecated
    */
+  @Deprecated
   public int getMaxLines()
   {
     return max_lines;
@@ -243,6 +244,7 @@ public class RegexReader extends Reader
    * 
    * @deprecated
    */
+  @Deprecated
   public void setMaxLines(int ml)
   {
     max_lines = ml;
@@ -255,6 +257,7 @@ public class RegexReader extends Reader
    * 
    * @deprecated
    */
+  @Deprecated
   public char getEOLchar()
   {
     return EOLchar;
@@ -265,6 +268,7 @@ public class RegexReader extends Reader
    * 
    * @deprecated
    */
+  @Deprecated
   public void setEOLchar(char c)
   {
     EOLchar = c;
index 31fa2ba..c99bfea 100755 (executable)
@@ -43,13 +43,13 @@ public class RegexTokenizer implements Enumeration
     if (r.searchFrom(toParse, pos))
     {
       v.addElement(r.left().substring(pos));
-      vi.addElement(new Integer(r.matchFrom() + r.charsMatched()));
+      vi.addElement(Integer.valueOf(r.matchFrom() + r.charsMatched()));
       for (int i = 0; i < r.numSubs(); i++)
       {
         if (r.substring() != null)
         {
           v.addElement(r.substring(i + offset));
-          vi.addElement(new Integer(r.matchFrom(i + offset)
+          vi.addElement(Integer.valueOf(r.matchFrom(i + offset)
                   + r.charsMatched(i + offset)));
         }
       }
index 1d57a31..6b5e36c 100644 (file)
@@ -173,7 +173,7 @@ public abstract class ChimUtils
       float[] rgbValues = new float[4];
       for (int i = 0; i < rgbStrings.length; i++)
       {
-        Float f = new Float(rgbStrings[i]);
+        Float f = Float.valueOf(rgbStrings[i]);
         rgbValues[i] = f.floatValue();
       }
       if (rgbStrings.length == 4)
@@ -203,7 +203,7 @@ public abstract class ChimUtils
    */
   public static Integer makeModelKey(int model, int subModel)
   {
-    return new Integer(model * MAX_SUB_MODELS + subModel);
+    return Integer.valueOf(model * MAX_SUB_MODELS + subModel);
   }
 
   // invoked by the getResdiue (parseConnectivityReplies in
index 9a29a99..4f871d3 100644 (file)
@@ -200,7 +200,7 @@ public class ChimeraChain implements ChimeraStructuralObject
    */
   public ChimeraResidue getResidue(String index)
   {
-    // Integer index = new Integer(residueIndex);
+    // Integer index = Integer.valueOf(residueIndex);
     if (residueMap.containsKey(index))
       return residueMap.get(index);
     return null;
index 09a9713..22c9098 100644 (file)
@@ -121,8 +121,8 @@ public class StructureManager
       for (String chimObjName : names)
       {
         // get or open the corresponding models if they already exist
-        List<ChimeraModel> currentModels = chimeraManager.getChimeraModels(
-                chimObjName, type);
+        List<ChimeraModel> currentModels = chimeraManager
+                .getChimeraModels(chimObjName, type);
         if (currentModels.size() == 0)
         {
           // open and return models
@@ -562,11 +562,11 @@ public class StructureManager
         // Get the corresponding "real" model
         if (chimeraManager.hasChimeraModel(modelNumber, subModelNumber))
         {
-          ChimeraModel dataModel = chimeraManager.getChimeraModel(
-                  modelNumber, subModelNumber);
-          if (dataModel.getResidueCount() == selectedModel
-                  .getResidueCount()
-                  || dataModel.getModelType() == StructureManager.ModelType.SMILES)
+          ChimeraModel dataModel = chimeraManager
+                  .getChimeraModel(modelNumber, subModelNumber);
+          if (dataModel.getResidueCount() == selectedModel.getResidueCount()
+                  || dataModel
+                          .getModelType() == StructureManager.ModelType.SMILES)
           {
             // Select the entire model
             addChimSelection(dataModel);
@@ -576,8 +576,8 @@ public class StructureManager
           {
             for (ChimeraChain selectedChain : selectedModel.getChains())
             {
-              ChimeraChain dataChain = dataModel.getChain(selectedChain
-                      .getChainId());
+              ChimeraChain dataChain = dataModel
+                      .getChain(selectedChain.getChainId());
               if (selectedChain.getResidueCount() == dataChain
                       .getResidueCount())
               {
@@ -931,6 +931,7 @@ public class StructureManager
       pathList.add("/usr/local/chimera/bin/chimera");
       pathList.add("/usr/local/bin/chimera");
       pathList.add("/usr/bin/chimera");
+      pathList.add(System.getProperty("user.home") + "/opt/bin/chimera");
     }
     else if (os.startsWith("Windows"))
     {
index 61f3b7f..52dedcf 100755 (executable)
@@ -580,6 +580,7 @@ public class AAFrequency
           {
             codonCounts[codonEncoded + 2]++;
             ungappedCount++;
+            break;
           }
         }
       }
index 7ecce49..b88ef3a 100755 (executable)
@@ -586,7 +586,7 @@ public class AlignmentSorter
 
     for (int i = 0; i < alignment.length; i++)
     {
-      ids[i] = (new Float(alignment[i].getName().substring(8)))
+      ids[i] = (Float.valueOf(alignment[i].getName().substring(8)))
               .floatValue();
     }
 
index 2946ba2..9b8d220 100644 (file)
@@ -1871,7 +1871,7 @@ public class AlignmentUtils
       return;
     }
 
-    MapList newMap = targetToFrom.traverse(fromLoci.getMap());
+    MapList newMap = targetToFrom.traverse(fromLoci.getMapping());
 
     if (newMap != null)
     {
index bd87ee8..ff38c08 100755 (executable)
@@ -698,7 +698,7 @@ public class Conservation
 
       max = Math.max(max, bigtot);
 
-      quality.addElement(new Double(bigtot));
+      quality.addElement(Double.valueOf(bigtot));
     }
 
     double newmax = -Double.MAX_VALUE;
@@ -710,7 +710,7 @@ public class Conservation
       tmp = ((max - tmp) * (size - cons2GapCounts[j])) / size;
 
       // System.out.println(tmp+ " " + j);
-      quality.setElementAt(new Double(tmp), j);
+      quality.setElementAt(Double.valueOf(tmp), j);
 
       if (tmp > newmax)
       {
index 629a8a3..4b68d93 100644 (file)
@@ -134,7 +134,7 @@ public class ParseProperties
           double score = Double.NaN;
           try
           {
-            score = new Double(sstring).doubleValue();
+            score = Double.valueOf(sstring).doubleValue();
           } catch (Exception e)
           {
             // don't try very hard to parse if regex was wrong.
index fabd0c6..fdca89d 100755 (executable)
@@ -45,8 +45,8 @@ public class SeqsetUtils
   {
     Hashtable sqinfo = new Hashtable();
     sqinfo.put("Name", seq.getName());
-    sqinfo.put("Start", new Integer(seq.getStart()));
-    sqinfo.put("End", new Integer(seq.getEnd()));
+    sqinfo.put("Start", Integer.valueOf(seq.getStart()));
+    sqinfo.put("End", Integer.valueOf(seq.getEnd()));
     if (seq.getDescription() != null)
     {
       sqinfo.put("Description", seq.getDescription());
index cc97be0..c04df6c 100644 (file)
@@ -226,14 +226,14 @@ public class StructureFrequency
           maxResidue = "{";
         }
       }
-      residueHash.put(MAXCOUNT, new Integer(count));
+      residueHash.put(MAXCOUNT, Integer.valueOf(count));
       residueHash.put(MAXRESIDUE, maxResidue);
 
       percentage = ((float) count * 100) / jSize;
-      residueHash.put(PID_GAPS, new Float(percentage));
+      residueHash.put(PID_GAPS, Float.valueOf(percentage));
 
       percentage = ((float) count * 100) / nongap;
-      residueHash.put(PID_NOGAPS, new Float(percentage));
+      residueHash.put(PID_NOGAPS, Float.valueOf(percentage));
 
       if (result[i] == null)
       {
@@ -260,14 +260,14 @@ public class StructureFrequency
           residueHash.put(PAIRPROFILE, pairs);
         }
 
-        residueHash.put(MAXCOUNT, new Integer(count));
+        residueHash.put(MAXCOUNT, Integer.valueOf(count));
         residueHash.put(MAXRESIDUE, maxResidue);
 
         percentage = ((float) count * 100) / jSize;
-        residueHash.put(PID_GAPS, new Float(percentage));
+        residueHash.put(PID_GAPS, Float.valueOf(percentage));
 
         percentage = ((float) count * 100) / nongap;
-        residueHash.put(PID_NOGAPS, new Float(percentage));
+        residueHash.put(PID_NOGAPS, Float.valueOf(percentage));
 
         result[bpEnd] = residueHash;
       }
index e506be2..8545e94 100644 (file)
@@ -58,7 +58,7 @@ public class FeatureDistanceModel extends DistanceScoreModel
     FeatureDistanceModel instance;
     try
     {
-      instance = this.getClass().newInstance();
+      instance = this.getClass().getDeclaredConstructor().newInstance();
       instance.configureFromAlignmentView(view);
       return instance;
     } catch (InstantiationException | IllegalAccessException e)
@@ -66,6 +66,9 @@ public class FeatureDistanceModel extends DistanceScoreModel
       System.err.println("Error in " + getClass().getName()
               + ".getInstance(): " + e.getMessage());
       return null;
+    } catch (ReflectiveOperationException roe)
+    {
+      return null;
     }
   }
 
@@ -188,7 +191,7 @@ public class FeatureDistanceModel extends DistanceScoreModel
   protected Map<SeqCigar, Set<String>> findFeatureTypesAtColumn(
           SeqCigar[] seqs, int columnPosition)
   {
-    Map<SeqCigar, Set<String>> sfap = new HashMap<SeqCigar, Set<String>>();
+    Map<SeqCigar, Set<String>> sfap = new HashMap<>();
     for (SeqCigar seq : seqs)
     {
       int spos = seq.findPosition(columnPosition);
@@ -197,7 +200,7 @@ public class FeatureDistanceModel extends DistanceScoreModel
         /*
          * position is not a gap
          */
-        Set<String> types = new HashSet<String>();
+        Set<String> types = new HashSet<>();
         List<SequenceFeature> sfs = fr.findFeaturesAtResidue(
                 seq.getRefSeq(), spos);
         for (SequenceFeature sf : sfs)
index dcd3258..065be75 100644 (file)
@@ -433,9 +433,19 @@ public interface AlignViewportI extends ViewStyleI
    */
   void setFollowHighlight(boolean b);
 
+  /**
+   * configure the feature renderer with predefined feature settings
+   * 
+   * @param featureSettings
+   */
   public void applyFeaturesStyle(FeatureSettingsModelI featureSettings);
 
   /**
+   * Apply the given feature settings on top of existing feature settings.
+   */
+  public void mergeFeaturesStyle(FeatureSettingsModelI featureSettings);
+
+  /**
    * check if current selection group is defined on the view, or is simply a
    * temporary group.
    * 
index 7bfd8a8..98a37c4 100644 (file)
@@ -205,9 +205,9 @@ public interface FeatureColourI
    */
   boolean isOutwithThreshold(SequenceFeature sf);
 
-  /*
-   * Answers a human-readable text description of the colour, suitable for
-   * display as a tooltip, possibly internationalised for the user's locale.
+  /**
+   * Answers a human-readable text description of the colour, suitable for display
+   * as a tooltip, possibly internationalised for the user's locale.
    * 
    * @return
    */
index 2cf33ac..f3fdd67 100644 (file)
@@ -106,6 +106,7 @@ import java.awt.event.KeyListener;
 import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.net.URL;
 import java.net.URLEncoder;
 import java.util.Arrays;
@@ -1569,7 +1570,7 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     try
     {
       new URL(url);
-      url = URLEncoder.encode(url);
+      url = URLEncoder.encode(url, "UTF-8");
     }
     /*
      * When we finally deprecate 1.1 compatibility, we can start to use
@@ -1582,6 +1583,13 @@ public class AlignFrame extends EmbmenuFrame implements ActionListener,
     {
       url = viewport.applet.getCodeBase() + url;
     }
+    catch (UnsupportedEncodingException ex)
+    {
+      System.err.println(
+              "WARNING = IMPLEMENTATION ERROR - UNSUPPORTED ENCODING EXCEPTION FOR "
+                      + url);
+      ex.printStackTrace();
+    }
     return url;
   }
 
index 2bead46..0324d8c 100644 (file)
@@ -71,7 +71,7 @@ public class AlignViewport extends AlignmentViewport
       {
         try
         {
-          widthScale = new Float(param).floatValue();
+          widthScale = Float.valueOf(param).floatValue();
         } catch (Exception e)
         {
         }
@@ -94,7 +94,7 @@ public class AlignViewport extends AlignmentViewport
       {
         try
         {
-          heightScale = new Float(param).floatValue();
+          heightScale = Float.valueOf(param).floatValue();
         } catch (Exception e)
         {
         }
@@ -392,10 +392,10 @@ public class AlignViewport extends AlignmentViewport
   }
 
   /**
-   * Applies the supplied feature settings descriptor to currently known
-   * features. This supports an 'initial configuration' of feature colouring
-   * based on a preset or user favourite. This may then be modified in the usual
-   * way using the Feature Settings dialogue.
+   * Applies the supplied feature settings descriptor to currently known features.
+   * This supports an 'initial configuration' of feature colouring based on a
+   * preset or user favourite. This may then be modified in the usual way using
+   * the Feature Settings dialogue. NOT IMPLEMENTED FOR APPLET
    * 
    * @param featureSettings
    */
@@ -405,4 +405,18 @@ public class AlignViewport extends AlignmentViewport
     // TODO implement for applet
   }
 
+  /**
+   * Merges the supplied feature settings descriptor with existing feature styles.
+   * This supports an 'initial configuration' of feature colouring based on a
+   * preset or user favourite. This may then be modified in the usual way using
+   * the Feature Settings dialogue. NOT IMPLEMENTED FOR APPLET
+   * 
+   * @param featureSettings
+   */
+  @Override
+  public void mergeFeaturesStyle(FeatureSettingsModelI featureSettings)
+  {
+    // TODO Auto-generated method stub
+
+  }
 }
index 533226e..9456986 100644 (file)
@@ -350,7 +350,7 @@ public class AnnotationColourChooser extends Panel implements
     {
       try
       {
-        float f = new Float(thresholdValue.getText()).floatValue();
+        float f = Float.valueOf(thresholdValue.getText()).floatValue();
         slider.setValue((int) (f * 1000));
         adjustmentValueChanged(null);
       } catch (NumberFormatException ex)
index 1366f31..a0102b9 100755 (executable)
@@ -457,8 +457,8 @@ public class AnnotationLabels extends Panel
             .getAlignmentAnnotation();
 
     // DETECT RIGHT MOUSE BUTTON IN AWT
-    if ((evt.getModifiers()
-            & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
+    if ((evt.getModifiersEx()
+            & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK)
     {
 
       PopupMenu popup = new PopupMenu(
index 26d3a3b..3dae998 100755 (executable)
@@ -354,8 +354,8 @@ public class AnnotationPanel extends Panel
       }
     }
 
-    if ((evt.getModifiers()
-            & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK
+    if ((evt.getModifiersEx()
+            & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK
             && activeRow != -1)
     {
       if (av.getColumnSelection() == null
index b6b81fa..8b2b279 100644 (file)
@@ -51,8 +51,8 @@ import java.util.Map;
  * @author Jim Procter and Andrew Waterhouse
  * 
  */
-@SuppressWarnings("serial")
-public class EmbmenuFrame extends Frame implements MouseListener
+public class EmbmenuFrame extends Frame
+        implements MouseListener, AutoCloseable
 {
   protected static final Font FONT_ARIAL_PLAIN_11 = new Font("Arial",
           Font.PLAIN, 11);
@@ -62,7 +62,7 @@ public class EmbmenuFrame extends Frame implements MouseListener
   /**
    * map from labels to popup menus for the embedded menubar
    */
-  protected Map<Label, PopupMenu> embeddedPopup = new HashMap<Label, PopupMenu>();
+  protected Map<Label, PopupMenu> embeddedPopup = new HashMap<>();
 
   /**
    * the embedded menu is built on this and should be added to the frame at the
@@ -202,6 +202,7 @@ public class EmbmenuFrame extends Frame implements MouseListener
     return embeddedMenu;
   }
 
+  @Override
   public void mousePressed(MouseEvent evt)
   {
     PopupMenu popup = null;
@@ -226,18 +227,22 @@ public class EmbmenuFrame extends Frame implements MouseListener
     return embeddedPopup.get(source);
   }
 
+  @Override
   public void mouseClicked(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseReleased(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseEntered(MouseEvent evt)
   {
   }
 
+  @Override
   public void mouseExited(MouseEvent evt)
   {
   }
@@ -265,11 +270,11 @@ public class EmbmenuFrame extends Frame implements MouseListener
   /**
    * calls destroyMenus()
    */
-  public void finalize() throws Throwable
+  @Override
+  public void close()
   {
     destroyMenus();
     embeddedPopup = null;
     embeddedMenu = null;
-    super.finalize();
   }
 }
index 5569ab0..0d70660 100644 (file)
@@ -311,7 +311,7 @@ public class FeatureColourChooser extends Panel implements ActionListener,
   {
     try
     {
-      float f = new Float(thresholdValue.getText()).floatValue();
+      float f = Float.valueOf(thresholdValue.getText()).floatValue();
       slider.setValue((int) (f * SCALE_FACTOR_1K));
       adjustmentValueChanged(null);
 
index a60aacd..489cbb1 100755 (executable)
@@ -703,7 +703,7 @@ public class FeatureSettings extends Panel
   public void mouseClicked(MouseEvent evt)
   {
     MyCheckbox check = (MyCheckbox) evt.getSource();
-    if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
+    if ((evt.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0)
     {
       this.popupSort(check, fr.getMinMax(), evt.getX(), evt.getY());
     }
index 443ebce..8b74e32 100644 (file)
@@ -31,9 +31,9 @@ import java.awt.FlowLayout;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Frame;
+import java.awt.GraphicsEnvironment;
 import java.awt.Label;
 import java.awt.Panel;
-import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
@@ -144,7 +144,9 @@ public class FontChooser extends Panel implements ItemListener
    */
   void init()
   {
-    String fonts[] = Toolkit.getDefaultToolkit().getFontList();
+    // String fonts[] = Toolkit.getDefaultToolkit().getFontList();
+    String fonts[] = GraphicsEnvironment.getLocalGraphicsEnvironment()
+            .getAvailableFontFamilyNames();
     for (int i = 0; i < fonts.length; i++)
     {
       fontName.addItem(fonts[i]);
index af1c47b..1d37d08 100755 (executable)
@@ -279,8 +279,8 @@ public class IdPanel extends Panel
 
     int seq = alignPanel.seqPanel.findSeq(e);
 
-    if ((e.getModifiers()
-            & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
+    if ((e.getModifiersEx()
+            & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK)
     {
       SequenceI sq = av.getAlignment().getSequenceAt(seq);
 
index 4e69399..9cbdd36 100755 (executable)
@@ -116,8 +116,8 @@ public class OverviewPanel extends Panel implements Runnable,
   @Override
   public void mouseClicked(MouseEvent evt)
   {
-    if ((evt.getModifiers()
-            & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
+    if ((evt.getModifiersEx()
+            & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK)
     {
       showPopupMenu(evt);
     }
@@ -142,8 +142,8 @@ public class OverviewPanel extends Panel implements Runnable,
   @Override
   public void mousePressed(MouseEvent evt)
   {
-    if ((evt.getModifiers()
-            & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
+    if ((evt.getModifiersEx()
+            & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK)
     {
       if (!Platform.isMac()) // BH was excluding JavaScript
       {
index c91449f..d3f4a69 100755 (executable)
@@ -96,8 +96,8 @@ public class ScalePanel extends Panel
 
     min = res;
     max = res;
-    if ((evt.getModifiers()
-            & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
+    if ((evt.getModifiersEx()
+            & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK)
     {
       rightMouseButtonPressed(evt, res);
     }
index 32f0abb..5becbc1 100644 (file)
@@ -500,7 +500,6 @@ public class SeqPanel extends Panel implements MouseMotionListener,
 
     // For now, ignore the mouseWheel font resizing on Macs
     // As the Button2_mask always seems to be true
-    
     if (Platform.isWinMiddleButton(evt))
     {
       mouseWheelPressed = true;
@@ -1432,8 +1431,8 @@ public class SeqPanel extends Panel implements MouseMotionListener,
     }
 
     // DETECT RIGHT MOUSE BUTTON IN AWT
-    if ((evt.getModifiers()
-            & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
+    if ((evt.getModifiersEx()
+            & InputEvent.BUTTON3_DOWN_MASK) == InputEvent.BUTTON3_DOWN_MASK)
     {
       List<SequenceFeature> allFeatures = findFeaturesAtColumn(sequence,
               sequence.findPosition(column + 1));
index b5e3342..671fee1 100644 (file)
@@ -46,7 +46,7 @@ import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 
 public class TreePanel extends EmbmenuFrame
-        implements ActionListener, ItemListener
+        implements ActionListener, ItemListener, AutoCloseable
 {
   SequenceI[] seq;
 
@@ -72,11 +72,11 @@ public class TreePanel extends EmbmenuFrame
   }
 
   @Override
-  public void finalize() throws Throwable
+  public void close()
   {
     ap = null;
     av = null;
-    super.finalize();
+    super.close();
   }
 
   /**
index 1f183af..5f73227 100644 (file)
@@ -35,6 +35,13 @@ public class BuildDetails implements BuildDetailsI
 
   }
 
+  /**
+   * update singleton Strings with these build details
+   * 
+   * @param version
+   * @param buildDate
+   * @param installation
+   */
   public BuildDetails(String version, String buildDate, String installation)
   {
     BuildDetails.version = version;
@@ -42,6 +49,7 @@ public class BuildDetails implements BuildDetailsI
     BuildDetails.installation = installation;
   }
 
+  @Override
   public String getBuildDate()
   {
     return buildDate;
@@ -52,6 +60,7 @@ public class BuildDetails implements BuildDetailsI
     BuildDetails.buildDate = buildDate;
   }
 
+  @Override
   public String getVersion()
   {
     return version;
@@ -62,6 +71,7 @@ public class BuildDetails implements BuildDetailsI
     BuildDetails.version = version;
   }
 
+  @Override
   public String getInstallation()
   {
     return installation;
index 362fca8..9e28f5d 100755 (executable)
@@ -419,44 +419,7 @@ public class Cache
         applicationProperties.remove("YEAR");
     }
 
-    // FIND THE VERSION NUMBER AND BUILD DATE FROM jalview.jar
-    // MUST FOLLOW READING OF LOCAL PROPERTIES FILE AS THE
-    // VERSION MAY HAVE CHANGED SINCE LAST USING JALVIEW
-    String buildDetails = resolveResourceURLFor("/.build_properties");
-
-    if (buildDetails != null)
-    {
-      try
-      {
-        java.net.URL localJarFileURL = new java.net.URL(buildDetails);
-        InputStream in = localJarFileURL.openStream();
-        applicationProperties.load(in);
-        in.close();
-      } catch (Exception ex)
-      {
-        System.out.println("Error reading build details: " + ex);
-        buildDetails = null;
-      }
-    }
-    if (buildDetails == null)
-    {
-        applicationProperties.remove("VERSION");
-    }
-    String jnlpVersion = System.getProperty("jalview.version");
-    String codeVersion = getProperty("VERSION");
-    String codeInstallation = getProperty("INSTALLATION");
-    if (codeVersion == null)
-    {
-      // THIS SHOULD ONLY BE THE CASE WHEN TESTING!!
-      codeVersion = "Test";
-      jnlpVersion = "Test";
-      codeInstallation = "";
-    }
-    else
-    {
-      codeInstallation = " (" + codeInstallation + ")";
-    }
-    new BuildDetails(codeVersion, null, codeInstallation);
+    loadBuildProperties(false);
 
     SiftsSettings
             .setMapWithSifts(Cache.getDefault("MAP_WITH_SIFTS", false));
@@ -476,9 +439,6 @@ public class Cache
             "http://www.jalview.org/services/identifiers"));
     IdOrgSettings.setDownloadLocation(ID_ORG_FILE);
 
-    System.out
-            .println("Jalview Version: " + codeVersion + codeInstallation);
-
     StructureImportSettings.setDefaultStructureFileFormat(jalview.bin.Cache
             .getDefault("PDB_DOWNLOAD_FORMAT", PDB_DOWNLOAD_FORMAT));
     StructureImportSettings
@@ -486,14 +446,18 @@ public class Cache
     // StructureImportSettings
     // .setDefaultPDBFileParser(jalview.bin.Cache.getDefault(
     // "DEFAULT_PDB_FILE_PARSER", DEFAULT_PDB_FILE_PARSER));
-    // jnlpVersion will be null if we're using InstallAnywhere
+
+    String jnlpVersion = System.getProperty("jalview.version");
+
+    // jnlpVersion will be null if a latest version check for the channel needs to
+    // be done
     // Dont do this check if running in headless mode
+
     if (jnlpVersion == null && getDefault("VERSION_CHECK", true)
             && (System.getProperty("java.awt.headless") == null || System
                     .getProperty("java.awt.headless").equals("false")))
     {
-
-      class VersionChecker extends Thread
+      new Thread()
       {
         @Override
         public void run()
@@ -541,10 +505,7 @@ public class Cache
 
           setProperty("LATEST_VERSION", remoteVersion);
         }
-      }
-
-      VersionChecker vc = new VersionChecker();
-      vc.start();
+      }.start();
     }
     else
     {
@@ -558,16 +519,12 @@ public class Cache
       }
     }
 
-    setProperty("VERSION", codeVersion);
-
     // LOAD USERDEFINED COLOURS
-    jalview.bin.Cache
-            .initUserColourSchemes(getProperty("USER_DEFINED_COLOURS"));
+    Cache.initUserColourSchemes(getProperty("USER_DEFINED_COLOURS"));
     jalview.io.PIRFile.useModellerOutput = Cache.getDefault("PIR_MODELLER",
             false);
   }
 
-
   /**
    * construct a resource URL for the given absolute resource pathname
    * 
@@ -596,6 +553,50 @@ public class Cache
     return url;
   }
 
+  public static void loadBuildProperties(boolean reportVersion)
+  {
+    String codeInstallation = getProperty("INSTALLATION");
+    boolean printV = codeInstallation == null;
+
+    // FIND THE VERSION NUMBER AND BUILD DATE FROM jalview.jar
+    try
+    {
+      String buildDetails = "jar:".concat(Cache.class.getProtectionDomain()
+              .getCodeSource().getLocation().toString()
+              .concat("!/.build_properties"));
+
+      java.net.URL localJarFileURL = new java.net.URL(buildDetails);
+
+      InputStream in = localJarFileURL.openStream();
+      applicationProperties.load(in);
+      in.close();
+    } catch (Exception ex)
+    {
+      System.out.println("Error reading build details: " + ex);
+      applicationProperties.remove("VERSION");
+    }
+    String codeVersion = getProperty("VERSION");
+    codeInstallation = getProperty("INSTALLATION");
+
+    if (codeVersion == null)
+    {
+      // THIS SHOULD ONLY BE THE CASE WHEN TESTING!!
+      codeVersion = "Test";
+      codeInstallation = "";
+    }
+    else
+    {
+      codeInstallation = " (" + codeInstallation + ")";
+    }
+    setProperty("VERSION", codeVersion);
+    new BuildDetails(codeVersion, null, codeInstallation);
+    if (printV && reportVersion)
+    {
+      System.out
+            .println("Jalview Version: " + codeVersion + codeInstallation);
+    }
+  }
+
   private static void deleteBuildProperties()
   {
     applicationProperties.remove("LATEST_VERSION");
@@ -687,7 +688,7 @@ public class Cache
     try
     {
       oldValue = applicationProperties.setProperty(key, obj);
-      if (!propsAreReadOnly)
+      if (propertiesFile != null && !propsAreReadOnly)
       {
         FileOutputStream out = new FileOutputStream(propertiesFile);
         applicationProperties.store(out, "---JalviewX Properties File---");
@@ -1111,4 +1112,42 @@ public class Cache
     }
   }
 
+  /**
+   * 
+   * @return Jalview version, build details and JVM platform version for console
+   */
+  public static String getVersionDetailsForConsole()
+  {
+    return "Jalview Version: "
+            + jalview.bin.Cache.getDefault("VERSION", "TEST")
+            + "\n" + "Jalview Installation: "
+            + jalview.bin.Cache.getDefault("INSTALLATION",
+                    "unknown")
+            + "\n" + "Build Date: "
+            + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
+            + "\n" + "Java version: "
+            + System.getProperty("java.version") + "\n"
+            + System.getProperty("os.arch") + " "
+            + System.getProperty("os.name") + " "
+            + System.getProperty("os.version")
+            + (jalview.bin.Cache.getDefault("VERSION", "TEST")
+                    .equals("DEVELOPMENT")
+                                                               ? "\nGetdown appdir: " + System.getProperty("getdownappdir") + "\nJava path: "
+                                    + System.getProperty(
+                                            "java.home")
+                                    + File.separator + "bin"
+                                    + File.separator + "java"
+                                                               : "");
+  }
+
+  /**
+   * 
+   * @return build details as reported in splashscreen
+   */
+  public static String getBuildDetailsForSplash()
+  {
+    // consider returning more human friendly info
+    // eg 'built from Source' or update channel
+    return jalview.bin.Cache.getDefault("INSTALLATION", "unknown");
+  }
 }
index 694fe00..521b674 100755 (executable)
@@ -67,6 +67,8 @@ import java.util.logging.Logger;
 import javax.swing.LookAndFeel;
 import javax.swing.UIManager;
 
+import com.threerings.getdown.util.LaunchUtil;
+
 import groovy.lang.Binding;
 import groovy.util.GroovyScriptEngine;
 
@@ -137,8 +139,8 @@ public class Jalview
   class FeatureFetcher
   {
     /*
-     * TODO: generalise to track all jalview events to orchestrate batch
-     * processing events.
+     * TODO: generalise to track all jalview events to orchestrate batch processing
+     * events.
      */
 
     private int queued = 0;
@@ -196,7 +198,7 @@ public class Jalview
    * main class for Jalview application
    * 
    * @param args
-   *          open <em>filename</em>
+   *               open <em>filename</em>
    */
   public static void main(String[] args)
   {
@@ -264,6 +266,8 @@ public class Jalview
     System.out.println(System.getProperty("os.arch") + " "
             + System.getProperty("os.name") + " "
             + System.getProperty("os.version"));
+    // report Jalview version
+    Cache.loadBuildProperties(true);
 
     ArgsParser aparser = new ArgsParser(args);
     boolean headless = false;
@@ -377,8 +381,9 @@ public class Jalview
                   "Failed to set QuaQua look and feel: " + e.toString());
         }
       }
-      if (lookAndFeel == null || !(lookAndFeel.getClass()
-              .isAssignableFrom(UIManager.getLookAndFeel().getClass()))
+      if (lookAndFeel == null
+              || !(lookAndFeel.getClass().isAssignableFrom(
+                      UIManager.getLookAndFeel().getClass()))
               || !UIManager.getLookAndFeel().getClass().toString()
                       .toLowerCase().contains("quaqua"))
       {
@@ -396,10 +401,10 @@ public class Jalview
     }
 
     /*
-     * configure 'full' SO model if preferences say to, 
-     * else use the default (SO Lite)
+     * configure 'full' SO model if preferences say to, else use the default (SO
+     * Lite)
      */
-    if (Cache.getDefault("USE_FULL_SO", false))
+    if (Cache.getDefault("USE_FULL_SO", true))
     {
       SequenceOntologyFactory.setInstance(new SequenceOntology());
     }
@@ -417,6 +422,20 @@ public class Jalview
 //       }
       ;
       desktop.setInBatchMode(true); // indicate we are starting up
+
+      try
+      {
+        JalviewTaskbar.setTaskbar(this);
+      } catch (Exception e)
+      {
+        System.out.println("Cannot set Taskbar");
+        // e.printStackTrace();
+      } catch (Throwable t)
+      {
+        System.out.println("Cannot set Taskbar");
+        // t.printStackTrace();
+      }
+
       desktop.setVisible(true);
 
       if (!Platform.isJS())
@@ -479,6 +498,25 @@ public class Jalview
       }
     }
 
+    // Move any new getdown-launcher-new.jar into place over old
+    // getdown-launcher.jar
+    String appdirString = System.getProperty("getdownappdir");
+    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"));
+        }
+      }.start();
+    }
+
     String file = null, data = null;
     FileFormatI format = null;
     DataSourceType protocol = null;
@@ -495,91 +533,6 @@ public class Jalview
       System.out.println("No files to open!");
       System.exit(1);
     }
-    String vamsasImport = aparser.getValue("vdoc");
-    String vamsasSession = aparser.getValue("vsess");
-    if (vamsasImport != null || vamsasSession != null)
-    {
-      if (desktop == null || headless)
-      {
-        System.out.println(
-                "Headless vamsas sessions not yet supported. Sorry.");
-        System.exit(1);
-      }
-      // if we have a file, start a new session and import it.
-      boolean inSession = false;
-      if (vamsasImport != null)
-      {
-        try
-        {
-          DataSourceType viprotocol = AppletFormatAdapter
-                  .checkProtocol(vamsasImport);
-          if (viprotocol == DataSourceType.FILE)
-          {
-            inSession = desktop.vamsasImport(new File(vamsasImport));
-          }
-          else if (viprotocol == DataSourceType.URL)
-          {
-            inSession = desktop.vamsasImport(new URL(vamsasImport));
-          }
-
-        } catch (Exception e)
-        {
-          System.err.println("Exeption when importing " + vamsasImport
-                  + " as a vamsas document.");
-          e.printStackTrace();
-        }
-        if (!inSession)
-        {
-          System.err.println("Failed to import " + vamsasImport
-                  + " as a vamsas document.");
-        }
-        else
-        {
-          System.out.println("Imported Successfully into new session "
-                  + desktop.getVamsasApplication().getCurrentSession());
-        }
-      }
-      if (vamsasSession != null)
-      {
-        if (vamsasImport != null)
-        {
-          // close the newly imported session and import the Jalview specific
-          // remnants into the new session later on.
-          desktop.vamsasStop_actionPerformed(null);
-        }
-        // now join the new session
-        try
-        {
-          if (desktop.joinVamsasSession(vamsasSession))
-          {
-            System.out.println(
-                    "Successfully joined vamsas session " + vamsasSession);
-          }
-          else
-          {
-            System.err.println("WARNING: Failed to join vamsas session "
-                    + vamsasSession);
-          }
-        } catch (Exception e)
-        {
-          System.err.println(
-                  "ERROR: Failed to join vamsas session " + vamsasSession);
-          e.printStackTrace();
-        }
-        if (vamsasImport != null)
-        {
-          // the Jalview specific remnants can now be imported into the new
-          // session at the user's leisure.
-          Cache.log.info(
-                  "Skipping Push for import of data into existing vamsas session."); // TODO:
-          // enable
-          // this
-          // when
-          // debugged
-          // desktop.getVamsasApplication().push_update();
-        }
-      }
-    }
     long progress = -1;
     // Finally, deal with the remaining input data.
     if (file != null)
@@ -638,9 +591,8 @@ public class Jalview
         {
           data.replaceAll("%20", " ");
 
-          ColourSchemeI cs = ColourSchemeProperty
-                  .getColourScheme(af.getViewport(),
-                          af.getViewport().getAlignment(), data);
+          ColourSchemeI cs = ColourSchemeProperty.getColourScheme(
+                  af.getViewport(), af.getViewport().getAlignment(), data);
 
           if (cs != null)
           {
@@ -827,16 +779,15 @@ public class Jalview
     // ////////////////////
 
     if (!Platform.isJS() && !headless && file == null
-            && vamsasImport == null
-            && jalview.bin.Cache.getDefault("SHOW_STARTUP_FILE", true))
+            && Cache.getDefault("SHOW_STARTUP_FILE", true))
     /**
      * Java only
      * 
      * @j2sIgnore
      */
     {
-      file = jalview.bin.Cache.getDefault("STARTUP_FILE",
-              jalview.bin.Cache.getDefault("www.jalview.org",
+      file = Cache.getDefault("STARTUP_FILE",
+              Cache.getDefault("www.jalview.org",
                       "http://www.jalview.org")
                       + "/examples/exampleFile_2_7.jar");
       if (file.equals(
@@ -845,7 +796,7 @@ public class Jalview
         // hardwire upgrade of the startup file
         file.replace("_2_3.jar", "_2_7.jar");
         // and remove the stale setting
-        jalview.bin.Cache.removeProperty("STARTUP_FILE");
+        Cache.removeProperty("STARTUP_FILE");
       }
 
       protocol = DataSourceType.FILE;
@@ -938,10 +889,6 @@ public class Jalview
                     // passed in correctly)"
                     + "-jabaws URL\tSpecify URL for Jabaws services (e.g. for a local installation).\n"
                     + "-fetchfrom nickname\tQuery nickname for features for the alignments and display them.\n"
-                    // +
-                    // "-vdoc vamsas-document\tImport vamsas document into new
-                    // session or join existing session with same URN\n"
-                    // + "-vses vamsas-session\tJoin session with given URN\n"
                     + "-groovy FILE\tExecute groovy script in FILE, after all other arguments have been processed (if FILE is the text 'STDIN' then the file will be read from STDIN)\n"
                     + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
   }
@@ -981,10 +928,10 @@ public class Jalview
    * Locate the given string as a file and pass it to the groovy interpreter.
    * 
    * @param groovyscript
-   *          the script to execute
+   *                         the script to execute
    * @param jalviewContext
-   *          the Jalview Desktop object passed in to the groovy binding as the
-   *          'Jalview' object.
+   *                         the Jalview Desktop object passed in to the groovy
+   *                         binding as the 'Jalview' object.
    */
   private void executeGroovyScript(String groovyscript, AlignFrame af)
   {
@@ -1071,7 +1018,7 @@ public class Jalview
     }
     try
     {
-      Map<String, Object> vbinding = new HashMap<>();
+      Map<String, java.lang.Object> vbinding = new HashMap<>();
       vbinding.put("Jalview", this);
       if (af != null)
       {
@@ -1113,8 +1060,8 @@ public class Jalview
   }
 
   /**
-   * Quit method delegates to Desktop.quit - unless running in headless mode
-   * when it just ends the JVM
+   * Quit method delegates to Desktop.quit - unless running in headless mode when
+   * it just ends the JVM
    */
   public void quit()
   {
index e483fce..e7f2a53 100644 (file)
@@ -198,7 +198,7 @@ public class JalviewLite extends Applet
       int apos = -1;
       try
       {
-        apos = new Integer(position).intValue();
+        apos = Integer.valueOf(position).intValue();
         apos--;
       } catch (NumberFormatException ex)
       {
@@ -337,7 +337,7 @@ public class JalviewLite extends Applet
           int from = -1, to = -1;
           try
           {
-            from = new Integer(cl.substring(0, p)).intValue();
+            from = Integer.valueOf(cl.substring(0, p)).intValue();
             from--;
           } catch (NumberFormatException ex)
           {
@@ -348,7 +348,7 @@ public class JalviewLite extends Applet
           }
           try
           {
-            to = new Integer(cl.substring(p + 1)).intValue();
+            to = Integer.valueOf(cl.substring(p + 1)).intValue();
             to--;
           } catch (NumberFormatException ex)
           {
@@ -408,7 +408,7 @@ public class JalviewLite extends Applet
           int r = -1;
           try
           {
-            r = new Integer(cl).intValue();
+            r = Integer.valueOf(cl).intValue();
             r--;
           } catch (NumberFormatException ex)
           {
@@ -1176,7 +1176,7 @@ public class JalviewLite extends Applet
         try
         {
           StructureSelectionManager.getStructureSelectionManager(me)
-                  .mouseOverStructure(new Integer(pdbResNum).intValue(),
+                  .mouseOverStructure(Integer.valueOf(pdbResNum).intValue(),
                           chain, pdbfile);
           if (debug)
           {
@@ -1212,8 +1212,8 @@ public class JalviewLite extends Applet
       {
         try
         {
-          alf.scrollTo(new Integer(topRow).intValue(),
-                  new Integer(leftHandColumn).intValue());
+          alf.scrollTo(Integer.valueOf(topRow).intValue(),
+                  Integer.valueOf(leftHandColumn).intValue());
 
         } catch (Exception ex)
         {
@@ -1244,7 +1244,7 @@ public class JalviewLite extends Applet
       {
         try
         {
-          alf.scrollToRow(new Integer(topRow).intValue());
+          alf.scrollToRow(Integer.valueOf(topRow).intValue());
 
         } catch (Exception ex)
         {
@@ -1276,7 +1276,7 @@ public class JalviewLite extends Applet
       {
         try
         {
-          alf.scrollToColumn(new Integer(leftHandColumn).intValue());
+          alf.scrollToColumn(Integer.valueOf(leftHandColumn).intValue());
 
         } catch (Exception ex)
         {
diff --git a/src/jalview/bin/JalviewTaskbar.java b/src/jalview/bin/JalviewTaskbar.java
new file mode 100644 (file)
index 0000000..7dd0382
--- /dev/null
@@ -0,0 +1,39 @@
+package jalview.bin;
+
+import java.awt.Image;
+import java.awt.Taskbar;
+
+public class JalviewTaskbar
+{
+  public JalviewTaskbar()
+  {
+  }
+
+  protected static void setTaskbar(Jalview jalview)
+  {
+    
+    if (Taskbar.isTaskbarSupported())
+    {
+      Taskbar tb = Taskbar.getTaskbar();
+      if (tb.isSupported(Taskbar.Feature.ICON_IMAGE))
+      {
+        try
+        {
+          java.net.URL url = jalview.getClass()
+                  .getResource("/images/JalviewLogo_Huge.png");
+          if (url != null)
+          {
+            Image image = java.awt.Toolkit.getDefaultToolkit()
+                    .createImage(url);
+            tb.setIconImage(image);
+          }
+        } catch (Exception e)
+        {
+          System.out.println("Unable to setIconImage()");
+        }
+      }
+    }
+
+  }
+
+}
diff --git a/src/jalview/bin/Launcher.java b/src/jalview/bin/Launcher.java
new file mode 100644 (file)
index 0000000..412f119
--- /dev/null
@@ -0,0 +1,178 @@
+package jalview.bin;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.util.ArrayList;
+
+public class Launcher
+{
+
+  private final static String startClass = "jalview.bin.Jalview";
+
+  private final static int maxHeapSizePerCent = 90;
+
+  private final static String maxHeapSizePerCentProperty = "jvmmempc";
+
+  private final static String dockIconPath = "JalviewLogo_Huge.png";
+
+  public static void main(String[] args)
+  {
+    final String javaBin = System.getProperty("java.home") + File.separator
+            + "bin" + File.separator + "java";
+
+    ArrayList<String> command = new ArrayList<>();
+    command.add(javaBin);
+
+    String memSetting = null;
+
+    boolean isAMac = System.getProperty("os.name").indexOf("Mac") > -1;
+
+    for (String jvmArg : ManagementFactory.getRuntimeMXBean()
+            .getInputArguments())
+    {
+      command.add(jvmArg);
+    }
+    command.add("-cp");
+    command.add(ManagementFactory.getRuntimeMXBean().getClassPath());
+    ArrayList<String> arguments = new ArrayList<>();
+    for (String arg : args)
+    {
+      arguments.add(arg);
+    }
+
+    // add memory setting if not specified
+    boolean memSet = false;
+    boolean dockIcon = false;
+    boolean dockName = false;
+    ARG: for (int i = 0; i < command.size(); i++)
+    {
+      String arg = command.get(i);
+      if (arg.startsWith("-Xmx"))
+      {
+        memSetting = arg;
+        memSet = true;
+      }
+      else if (arg.startsWith("-Xdock:icon"))
+      {
+        dockIcon = true;
+      }
+      else if (arg.startsWith("-Xdock:name"))
+      {
+        dockName = true;
+      }
+    }
+
+    if (!memSet)
+    {
+      long maxMemLong = -1;
+      int percent = maxHeapSizePerCent;
+      String jvmmempc = System.getProperty(maxHeapSizePerCentProperty);
+      try
+      {
+        if (jvmmempc != null)
+        {
+          int trypercent = Integer.parseInt(jvmmempc);
+          if (0 < trypercent && trypercent <= 100)
+          {
+            percent = trypercent;
+          }
+          else
+          {
+            System.out.println("Property '" + maxHeapSizePerCentProperty
+                    + "' should be in range 1..100");
+          }
+        }
+      } catch (Exception e)
+      {
+        System.out.println("Error parsing " + maxHeapSizePerCentProperty
+                + " '" + jvmmempc + "'");
+      }
+
+      try
+      {
+        maxMemLong = MemorySetting.memPercent(percent);
+      } catch (Exception e)
+      {
+        e.printStackTrace();
+      } catch (Throwable t)
+      {
+        t.printStackTrace();
+      }
+
+      if (maxMemLong > 0)
+      {
+        memSetting = "-Xmx" + Long.toString(maxMemLong);
+        command.add(memSetting);
+      }
+    }
+
+    if (isAMac)
+    {
+      if (!dockIcon)
+      {
+        command.add("-Xdock:icon=" + dockIconPath);
+      }
+      if (!dockName)
+      {
+        // -Xdock:name=... doesn't actually work :(
+        // Leaving it in in case it gets fixed
+        command.add("-Xdock:name=" + "Jalview");
+      }
+    }
+
+    command.add(startClass);
+    command.addAll(arguments);
+
+    final ProcessBuilder builder = new ProcessBuilder(command);
+
+    // System.out.println("COMMAND: " + String.join(" ", builder.command()));
+    System.out.println("Running " + startClass + " with "
+            + (memSetting == null ? "no memSetting" : memSetting));
+
+    try
+    {
+      builder.inheritIO();
+      Process process = builder.start();
+      process.waitFor();
+    } catch (IOException e)
+    {
+      if (e.getMessage().toLowerCase().contains("memory"))
+      {
+        System.out.println("Caught a memory exception: " + e.getMessage());
+        // Probably the "Cannot allocate memory" error, try without the memory setting
+        ArrayList<String> commandNoMem = new ArrayList<>();
+        for (int i = 0; i < command.size(); i++)
+        {
+          if (!command.get(i).startsWith("-Xmx"))
+          {
+            commandNoMem.add(command.get(i));
+          }
+        }
+        final ProcessBuilder builderNoMem = new ProcessBuilder(
+                commandNoMem);
+        System.out.println("NO MEM COMMAND: "
+                + String.join(" ", builderNoMem.command()));
+        try
+        {
+          builderNoMem.inheritIO();
+          Process processNoMem = builderNoMem.start();
+          processNoMem.waitFor();
+        } catch (Exception ex)
+        {
+          ex.printStackTrace();
+        }
+      }
+      else
+      {
+        e.printStackTrace();
+      }
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    // System.exit(0);
+
+  }
+
+}
diff --git a/src/jalview/bin/MemorySetting.java b/src/jalview/bin/MemorySetting.java
new file mode 100644 (file)
index 0000000..c8bc222
--- /dev/null
@@ -0,0 +1,51 @@
+package jalview.bin;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+
+public class MemorySetting
+{
+  public static final long leaveFreeMinMemory = 536870912; // 0.5 GB
+
+  public static final long applicationMinMemory = 536870912; // 0.5 GB
+
+  protected static long getPhysicalMemory()
+  {
+    final OperatingSystemMXBean o = ManagementFactory
+            .getOperatingSystemMXBean();
+
+    try
+    {
+      if (o instanceof com.sun.management.OperatingSystemMXBean)
+      {
+        final com.sun.management.OperatingSystemMXBean osb = (com.sun.management.OperatingSystemMXBean) o;
+        return osb.getTotalPhysicalMemorySize();
+      }
+    } catch (NoClassDefFoundError e)
+    {
+      // com.sun.management.OperatingSystemMXBean doesn't exist in this JVM
+      Cache.log.error("No com.sun.management.OperatingSystemMXBean");
+    }
+
+    // We didn't get a com.sun.management.OperatingSystemMXBean.
+    return -1;
+  }
+
+  public static long memPercent(int percent)
+  {
+    long memPercent = -1;
+
+    long physicalMem = getPhysicalMemory();
+    if (physicalMem > applicationMinMemory)
+    {
+      // try and set at least applicationMinMemory and thereafter ensure
+      // leaveFreeMinMemory is left for the OS
+      memPercent = Math.max(applicationMinMemory,
+              physicalMem - Math.max(physicalMem * (100 - percent) / 100,
+                      leaveFreeMinMemory));
+    }
+
+    return memPercent;
+  }
+
+}
index f2b3904..d0790c8 100644 (file)
@@ -838,7 +838,7 @@ public class EditCommand implements CommandI
             // old ds and edited ds are different, so
             // create the new dataset sequence
             SequenceI newds = new Sequence(oldds);
-            newds.setSequence(fullseq);
+            newds.setSequence(fullseq.toUpperCase());
 
             if (command.oldds == null)
             {
index 33699cc..9b8a9e3 100644 (file)
@@ -166,9 +166,10 @@ public class AlignViewController implements AlignViewControllerI
     // JBPNote this routine could also mark rows, not just columns.
     // need a decent query structure to allow all types of feature searches
     BitSet bs = new BitSet();
-    SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null
-            || extendCurrent) ? viewport.getAlignment()
-                    : viewport.getSelectionGroup();
+    boolean searchSelection = viewport.getSelectionGroup() != null
+            && !extendCurrent;
+    SequenceCollectionI sqcol = searchSelection ? viewport
+            .getSelectionGroup() : viewport.getAlignment();
 
     int nseq = findColumnsWithFeature(featureType, sqcol, bs);
 
@@ -204,9 +205,10 @@ public class AlignViewController implements AlignViewControllerI
     }
     else
     {
-      avcg.setStatus(MessageManager
-              .formatMessage("label.no_feature_of_type_found", new String[]
-              { featureType }));
+      String key = searchSelection ? "label.no_feature_found_selection"
+              : "label.no_feature_of_type_found";
+      avcg.setStatus(MessageManager.formatMessage(key,
+              new String[] { featureType }));
       if (!extendCurrent)
       {
         cs.clear();
index 1956df7..c4098e2 100755 (executable)
@@ -47,7 +47,7 @@ import java.util.Vector;
  * @author JimP
  * 
  */
-public class Alignment implements AlignmentI
+public class Alignment implements AlignmentI, AutoCloseable
 {
   private Alignment dataset;
 
@@ -303,15 +303,20 @@ public class Alignment implements AlignmentI
   }
 
   @Override
-  public void finalize() throws Throwable
+  public void close()
   {
     if (getDataset() != null)
     {
-      getDataset().removeAlignmentRef();
+      try
+      {
+        getDataset().removeAlignmentRef();
+      } catch (Throwable e)
+      {
+        e.printStackTrace();
+      }
     }
 
     nullReferences();
-    super.finalize();
   }
 
   /**
index ee9389c..2ee4503 100755 (executable)
@@ -991,7 +991,7 @@ public class AlignmentAnnotation
           seqPos = i + startRes;
         }
 
-        sequenceMapping.put(new Integer(seqPos), annotations[i]);
+        sequenceMapping.put(Integer.valueOf(seqPos), annotations[i]);
       }
     }
 
@@ -1030,7 +1030,7 @@ public class AlignmentAnnotation
     {
       for (a = sequenceRef.getStart(); a <= sequenceRef.getEnd(); a++)
       {
-        index = new Integer(a);
+        index = Integer.valueOf(a);
         Annotation annot = sequenceMapping.get(index);
         if (annot != null)
         {
index c33abb3..cea9de7 100755 (executable)
@@ -169,7 +169,7 @@ public class BinarySequence extends Sequence
 
     for (int i = 0; i < binary.length; i++)
     {
-      out += (new Integer(binary[i])).toString();
+      out += (Integer.valueOf(binary[i])).toString();
 
       if (i < (binary.length - 1))
       {
index 3ccaab8..6f14e21 100644 (file)
@@ -106,7 +106,7 @@ public class ColumnSelection
     void remove(int col)
     {
 
-      Integer colInt = new Integer(col);
+      Integer colInt = Integer.valueOf(col);
 
       if (selected.get(col))
       {
@@ -204,7 +204,7 @@ public class ColumnSelection
           // clear shifted bits and update List of selected columns
           selected.clear(temp);
           mask.set(temp - change);
-          order.set(i, new Integer(temp - change));
+          order.set(i, Integer.valueOf(temp - change));
         }
       }
       // lastly update the bitfield all at once
@@ -309,7 +309,7 @@ public class ColumnSelection
     Integer colInt;
     for (int i = start; i < end; i++)
     {
-      colInt = new Integer(i);
+      colInt = Integer.valueOf(i);
       if (selection.contains(colInt))
       {
         selection.remove(colInt);
index 54e8379..f557ff8 100755 (executable)
@@ -21,7 +21,6 @@
 package jalview.datamodel;
 
 import jalview.api.DBRefEntryI;
-import jalview.io.vamsas.Dbref;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
 
@@ -29,16 +28,7 @@ import java.util.List;
 
 public class DBRefEntry implements DBRefEntryI
 {
-  /*
-   * the mapping to chromosome (genome) is held as an instance with
-   * source = speciesId
-   * version = assemblyId
-   * accessionId = "chromosome:" + chromosomeId
-   * map = mapping from sequence to reference assembly
-   */
-  public static final String CHROMOSOME = "chromosome";
-
-  private String source = "";
+  String source = "";
 
   private String version = "";
   
@@ -50,8 +40,7 @@ public class DBRefEntry implements DBRefEntryI
 
   String canonicalSourceName;
 
-
-  /**
+  /*
    * maps from associated sequence to the database sequence's coordinate system
    */
   Mapping map = null;
@@ -60,12 +49,16 @@ public class DBRefEntry implements DBRefEntryI
   {
 
   }
-/**
- * 
- * @param source may not be null
- * @param version may be null
- * @param accessionId may be null
- */
+
+  /**
+   * 
+   * @param source
+   *                      may not be null
+   * @param version
+   *                      may be null
+   * @param accessionId
+   *                      may be null
+   */
   public DBRefEntry(String source, String version, String accessionId)
   {
     this(source, version, accessionId, null);
@@ -380,16 +373,6 @@ public class DBRefEntry implements DBRefEntryI
   }
 
   /**
-   * Mappings to chromosome are held with accessionId as "chromosome:id"
-   * 
-   * @return
-   */
-  public boolean isChromosome()
-  {
-    return accessionId != null && accessionId.startsWith(CHROMOSOME + ":");
-  }
-
-  /**
    * stores the upper-case canonical name of the source for use in
    * Sequence.getPrimaryDBRefs().
    * 
@@ -400,6 +383,4 @@ public class DBRefEntry implements DBRefEntryI
   public String getCanonicalSourceName() {
        return (canonicalSourceName == null ? (canonicalSourceName = DBRefUtils.getCanonicalName(this.source)) : canonicalSourceName);
   }
-
-
 }
index f8c7ec5..09db9d7 100644 (file)
@@ -34,5 +34,5 @@ public interface GeneLociI
    * 
    * @return
    */
-  MapList getMap();
+  MapList getMapping();
 }
diff --git a/src/jalview/datamodel/GeneLocus.java b/src/jalview/datamodel/GeneLocus.java
new file mode 100644 (file)
index 0000000..f81348f
--- /dev/null
@@ -0,0 +1,91 @@
+package jalview.datamodel;
+
+import jalview.util.MapList;
+
+/**
+ * A specialisation of DBRefEntry used to hold the chromosomal coordinates for a
+ * (typically gene) sequence
+ * <ul>
+ * <li>field <code>source</code> is used to hold a species id e.g. human</li>
+ * <li>field <code>version</code> is used to hold assembly id e.g GRCh38</li>
+ * <li>field <code>accession</code> is used to hold the chromosome id</li>
+ * <li>field <code>map</code> is used to hold the mapping from sequence to
+ * chromosome coordinates</li>
+ * </ul>
+ * 
+ * @author gmcarstairs
+ *
+ */
+public class GeneLocus extends DBRefEntry implements GeneLociI
+{
+  /**
+   * Constructor adapts species, assembly, chromosome to DBRefEntry source,
+   * version, accession, respectively, and saves the mapping of sequence to
+   * chromosomal coordinates
+   * 
+   * @param speciesId
+   * @param assemblyId
+   * @param chromosomeId
+   * @param mapping
+   */
+  public GeneLocus(String speciesId, String assemblyId, String chromosomeId,
+          Mapping mapping)
+  {
+    super(speciesId, assemblyId, chromosomeId, mapping);
+  }
+
+  /**
+   * Constructor
+   * 
+   * @param speciesId
+   * @param assemblyId
+   * @param chromosomeId
+   */
+  public GeneLocus(String speciesId, String assemblyId, String chromosomeId)
+  {
+    this(speciesId, assemblyId, chromosomeId, null);
+  }
+
+  @Override
+  public boolean equals(Object o)
+  {
+    return o instanceof GeneLocus && super.equals(o);
+  }
+
+  @Override
+  public MapList getMapping()
+  {
+    return map == null ? null : map.getMap();
+  }
+
+  /**
+   * Answers the species identifier e.g. "human", stored as field <code>source</code> of
+   * DBRefEntry
+   */
+  @Override
+  public String getSpeciesId()
+  {
+    return getSource();
+  }
+
+  /**
+   * Answers the genome assembly id e.g. "GRCh38", stored as field
+   * <code>version</code> of DBRefEntry
+   */
+  @Override
+  public String getAssemblyId()
+  {
+    return getVersion();
+  }
+
+  /**
+   * Answers the chromosome identifier e.g. "X", stored as field
+   * <code>accession</code> of DBRefEntry
+   */
+  @Override
+  public String getChromosomeId()
+  {
+    return getAccessionId();
+  }
+
+}
index ca2b6d4..950ee05 100755 (executable)
@@ -696,8 +696,8 @@ public class Sequence extends ASequence implements SequenceI
   public void setGeneLoci(String speciesId, String assemblyId,
           String chromosomeId, MapList map)
   {
-    addDBRef(new DBRefEntry(speciesId, assemblyId,
-            DBRefEntry.CHROMOSOME + ":" + chromosomeId, new Mapping(map)));
+    addDBRef(new GeneLocus(speciesId, assemblyId, chromosomeId,
+            new Mapping(map)));
   }
 
   /**
@@ -713,41 +713,9 @@ public class Sequence extends ASequence implements SequenceI
     {
       for (final DBRefEntry ref : refs)
       {
-        if (ref.isChromosome())
+        if (ref instanceof GeneLociI)
         {
-          return new GeneLociI()
-          {
-            @Override
-            public String getSpeciesId()
-            {
-              return ref.getSource();
-            }
-
-            @Override
-            public String getAssemblyId()
-            {
-              // DEV NOTE: DBRefEntry is reused here to hold chromosomal locus
-              // of a gene sequence.
-              // source=species, version=assemblyId, accession=chromosome, map =
-              // positions.
-
-              return ref.getVersion();
-            }
-
-            @Override
-            public String getChromosomeId()
-            {
-              // strip off "chromosome:" prefix to chrId
-              return ref.getAccessionId()
-                      .substring(DBRefEntry.CHROMOSOME.length() + 1);
-            }
-
-            @Override
-            public MapList getMap()
-            {
-              return ref.getMap().getMap();
-            }
-          };
+          return (GeneLociI) ref;
         }
       }
     }
@@ -1612,7 +1580,7 @@ public class Sequence extends ASequence implements SequenceI
       _isNa = Comparison.isNucleotide(this);
     }
     return !_isNa;
-  };
+  }
 
   /*
    * (non-Javadoc)
index 2dd6ebb..e28cc7f 100644 (file)
@@ -129,6 +129,7 @@ class EnsemblFeatures extends EnsemblRestClient
           int end = Integer.parseInt(obj.get("end").toString());
           String source = obj.get("source").toString();
           String strand = obj.get("strand").toString();
+          Object phase = obj.get("phase");
           String alleles = JSONUtils
                   .arrayToStringList((List<Object>) obj.get("alleles"));
           String clinSig = JSONUtils
@@ -154,6 +155,10 @@ class EnsemblFeatures extends EnsemblRestClient
           SequenceFeature sf = new SequenceFeature(type, desc, start, end,
                   source);
           sf.setStrand("1".equals(strand) ? "+" : "-");
+          if (phase != null)
+          {
+            sf.setPhase(phase.toString());
+          }
           setFeatureAttribute(sf, obj, "id");
           setFeatureAttribute(sf, obj, "Parent");
           setFeatureAttribute(sf, obj, "consequence_type");
index 0d73a47..0e3d84b 100644 (file)
@@ -23,7 +23,6 @@ package jalview.ext.ensembl;
 import jalview.api.FeatureColourI;
 import jalview.api.FeatureSettingsModelI;
 import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.GeneLociI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
@@ -63,6 +62,8 @@ public class EnsemblGene extends EnsemblSeqProxy
       EnsemblFeatureType.exon, EnsemblFeatureType.cds,
       EnsemblFeatureType.variation };
 
+  private static final String CHROMOSOME = "chromosome";
+
   /**
    * Default constructor (to use rest.ensembl.org)
    */
@@ -185,7 +186,7 @@ public class EnsemblGene extends EnsemblSeqProxy
     if (geneLoci != null)
     {
       seq.setGeneLoci(geneLoci.getSpeciesId(), geneLoci.getAssemblyId(),
-              geneLoci.getChromosomeId(), geneLoci.getMap());
+              geneLoci.getChromosomeId(), geneLoci.getMapping());
     }
     else
     {
@@ -207,7 +208,7 @@ public class EnsemblGene extends EnsemblSeqProxy
       return false;
     }
     String[] tokens = description.split(":");
-    if (tokens.length == 6 && tokens[0].startsWith(DBRefEntry.CHROMOSOME))
+    if (tokens.length == 6 && tokens[0].startsWith(CHROMOSOME))
     {
       String ref = tokens[1];
       String chrom = tokens[2];
@@ -458,7 +459,7 @@ public class EnsemblGene extends EnsemblSeqProxy
       return;
     }
 
-    MapList geneMapping = loci.getMap();
+    MapList geneMapping = loci.getMapping();
 
     List<int[]> exons = mapping.getFromRanges();
     List<int[]> transcriptLoci = new ArrayList<>();
index fc37b8a..9b56d6b 100644 (file)
@@ -23,6 +23,8 @@ package jalview.ext.ensembl;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.GeneLociI;
+import jalview.datamodel.GeneLocus;
+import jalview.datamodel.Mapping;
 import jalview.util.MapList;
 
 import java.io.IOException;
@@ -268,34 +270,10 @@ public class EnsemblLookup extends EnsemblRestClient
           fromEnd });
       List<int[]> toRange = Collections.singletonList(new int[] { toStart,
           toEnd });
-      final MapList map = new MapList(fromRange, toRange, 1, 1);
-      return new GeneLociI()
-      {
-
-        @Override
-        public String getSpeciesId()
-        {
-          return species == null ? "" : species;
-        }
-
-        @Override
-        public String getAssemblyId()
-        {
-          return assembly;
-        }
-
-        @Override
-        public String getChromosomeId()
-        {
-          return chromosome;
-        }
-
-        @Override
-        public MapList getMap()
-        {
-          return map;
-        }
-      };
+      final Mapping map = new Mapping(
+              new MapList(fromRange, toRange, 1, 1));
+      return new GeneLocus(species == null ? "" : species, assembly,
+              chromosome, map);
     } catch (NullPointerException | NumberFormatException e)
     {
       Cache.log.error("Error looking up gene loci: " + e.getMessage());
index add71b3..e9cc9e1 100644 (file)
@@ -3,10 +3,10 @@ package jalview.ext.ensembl;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.GeneLociI;
-import jalview.util.JSONUtils;
+import jalview.datamodel.GeneLocus;
+import jalview.datamodel.Mapping;
 import jalview.util.MapList;
 
-import java.io.BufferedReader;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -18,6 +18,18 @@ import java.util.Map;
 
 import org.json.simple.parser.ParseException;
 
+/**
+ * A client for the Ensembl REST service /map endpoint, to convert from
+ * coordinates of one genome assembly to another.
+ * <p>
+ * Note that species and assembly identifiers passed to this class must be valid
+ * in Ensembl. They are not case sensitive.
+ * 
+ * @author gmcarstairs
+ * @see https://rest.ensembl.org/documentation/info/assembly_map
+ * @see https://rest.ensembl.org/info/assembly/human?content-type=text/xml
+ * @see https://rest.ensembl.org/info/species?content-type=text/xml
+ */
 public class EnsemblMap extends EnsemblRestClient
 {
   private static final String MAPPED = "mapped";
@@ -154,7 +166,9 @@ public class EnsemblMap extends EnsemblRestClient
     {
       Iterator<Object> rvals = (Iterator<Object>) getJSON(url, null, -1, MODE_ITERATOR, MAPPINGS);
       if (rvals == null)
-         return null;
+      {
+        return null;
+      }
       while (rvals.hasNext())
       {
         // todo check for "mapped"
@@ -286,7 +300,9 @@ GeneLociI parseIdMappingResponse(URL url, String accession,
     {
       Iterator<Object> rvals = (Iterator<Object>) getJSON(url, null, -1, MODE_ITERATOR, MAPPINGS);
       if (rvals == null)
-         return null;
+      {
+        return null;
+      }
       String assembly = null;
       String chromosome = null;
       int fromEnd = 0;
@@ -338,34 +354,9 @@ GeneLociI parseIdMappingResponse(URL url, String accession,
       final String chr = chromosome;
       List<int[]> fromRange = Collections.singletonList(new int[] { 1,
           fromEnd });
-      final MapList map = new MapList(fromRange, regions, 1, 1);
-      return new GeneLociI()
-      {
-
-        @Override
-        public String getSpeciesId()
-        {
-          return species == null ? "" : species;
-        }
-
-        @Override
-        public String getAssemblyId()
-        {
-          return as;
-        }
-
-        @Override
-        public String getChromosomeId()
-        {
-          return chr;
-        }
-
-        @Override
-        public MapList getMap()
-        {
-          return map;
-        }
-      };
+      Mapping mapping = new Mapping(new MapList(fromRange, regions, 1, 1));
+      return new GeneLocus(species == null ? "" : species, as, chr,
+              mapping);
     } catch (IOException | ParseException | NumberFormatException e)
     {
       // ignore
index 14c057f..2859e0f 100644 (file)
@@ -1,14 +1,16 @@
 package jalview.ext.htsjdk;
 
-import htsjdk.samtools.util.CloseableIterator;
-import htsjdk.variant.variantcontext.VariantContext;
-import htsjdk.variant.vcf.VCFFileReader;
-import htsjdk.variant.vcf.VCFHeader;
+import jalview.bin.Cache;
 
 import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
 
+import htsjdk.samtools.util.CloseableIterator;
+import htsjdk.variant.variantcontext.VariantContext;
+import htsjdk.variant.vcf.VCFFileReader;
+import htsjdk.variant.vcf.VCFHeader;
+
 /**
  * A thin wrapper for htsjdk classes to read either plain, or compressed, or
  * compressed and indexed VCF files
@@ -19,36 +21,52 @@ public class VCFReader implements Closeable, Iterable<VariantContext>
 
   private static final String TBI_EXTENSION = ".tbi";
 
+  private static final String CSI_EXTENSION = ".csi";
+
   private boolean indexed;
 
   private VCFFileReader reader;
 
   /**
-   * Constructor given a raw or compressed VCF file or a (tabix) index file
+   * Constructor given a raw or compressed VCF file or a (csi or tabix) index file
    * <p>
-   * For now, file type is inferred from its suffix: .gz or .bgz for compressed
-   * data, .tbi for an index file, anything else is assumed to be plain text
-   * VCF.
+   * If the file path ends in ".tbi" or ".csi", <em>or</em> appending one of these
+   * extensions gives a valid file path, open as indexed, else as unindexed.
    * 
    * @param f
    * @throws IOException
    */
   public VCFReader(String filePath) throws IOException
   {
-    if (filePath.endsWith(GZ))
+    indexed = false;
+    if (filePath.endsWith(TBI_EXTENSION)
+            || filePath.endsWith(CSI_EXTENSION))
     {
-      if (new File(filePath + TBI_EXTENSION).exists())
-      {
-        indexed = true;
-      }
+      indexed = true;
+      filePath = filePath.substring(0, filePath.length() - 4);
     }
-    else if (filePath.endsWith(TBI_EXTENSION))
+    else if (new File(filePath + TBI_EXTENSION).exists())
+    {
+      indexed = true;
+    }
+    else if (new File(filePath + CSI_EXTENSION).exists())
     {
       indexed = true;
-      filePath = filePath.substring(0, filePath.length() - 4);
     }
 
-    reader = new VCFFileReader(new File(filePath), indexed);
+    /*
+     * we pass the name of the unindexed file to htsjdk,
+     * with a flag to assert whether it is indexed
+     */
+    File file = new File(filePath);
+    if (file.exists())
+    {
+      reader = new VCFFileReader(file, indexed);
+    }
+    else
+    {
+      Cache.log.error("File not found: " + filePath);
+    }
   }
 
   @Override
@@ -88,9 +106,10 @@ public class VCFReader implements Closeable, Iterable<VariantContext>
   public CloseableIterator<VariantContext> query(final String chrom,
           final int start, final int end)
   {
-   if (reader == null) {
-     return null;
-   }
+    if (reader == null)
+    {
+      return null;
+    }
     if (indexed)
     {
       return reader.query(chrom, start, end);
@@ -145,7 +164,7 @@ public class VCFReader implements Closeable, Iterable<VariantContext>
           int vend = variant.getEnd();
           // todo what is the undeprecated way to get
           // the chromosome for the variant?
-          if (chrom.equals(variant.getChr()) && (vstart <= end)
+          if (chrom.equals(variant.getContig()) && (vstart <= end)
                   && (vend >= start))
           {
             return variant;
index fa31fd9..4698e4d 100644 (file)
@@ -44,7 +44,6 @@ import java.awt.event.ComponentEvent;
 import java.awt.event.ComponentListener;
 import java.io.File;
 import java.net.URL;
-import java.security.AccessControlException;
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Hashtable;
@@ -619,74 +618,6 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
    */
   private int _modelFileNameMap[];
 
-  // ////////////////////////////////
-  // /StructureListener
-  // @Override
-  public synchronized String[] getPdbFilex()
-  {
-    if (viewer == null)
-    {
-      return new String[0];
-    }
-    if (modelFileNames == null)
-    {
-      List<String> mset = new ArrayList<>();
-      _modelFileNameMap = new int[viewer.ms.mc];
-      String m = viewer.ms.getModelFileName(0);
-      if (m != null)
-      {
-        String filePath = m;
-        try
-        {
-          filePath = new File(m).getAbsolutePath();
-        } catch (AccessControlException x)
-        {
-          // usually not allowed to do this in applet
-          System.err.println(
-                  "jmolBinding: Using local file string from Jmol: " + m);
-        }
-        if (filePath.indexOf("/file:") != -1)
-        {
-          // applet path with docroot - discard as format won't match pdbfile
-          filePath = m;
-        }
-        mset.add(filePath);
-        _modelFileNameMap[0] = 0; // filename index for first model is always 0.
-      }
-      int j = 1;
-      for (int i = 1; i < viewer.ms.mc; i++)
-      {
-        m = viewer.ms.getModelFileName(i);
-        String filePath = m;
-        if (m != null)
-        {
-          try
-          {
-            filePath = new File(m).getAbsolutePath();
-          } catch (AccessControlException x)
-          {
-            // usually not allowed to do this in applet, so keep raw handle
-            // System.err.println("jmolBinding: Using local file string from
-            // Jmol: "+m);
-          }
-        }
-
-        /*
-         * add this model unless it is read from a structure file we have
-         * already seen (example: 2MJW is an NMR structure with 10 models)
-         */
-        if (!mset.contains(filePath))
-        {
-          mset.add(filePath);
-          _modelFileNameMap[j] = i; // record the model index for the filename
-          j++;
-        }
-      }
-      modelFileNames = mset.toArray(new String[mset.size()]);
-    }
-    return modelFileNames;
-  }
-
   @Override
   public synchronized String[] getStructureFiles()
   {
@@ -881,7 +812,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       try
       {
         // recover PDB filename for the model hovered over.
-        int mnumber = new Integer(mdlId).intValue() - 1;
+        int mnumber = Integer.valueOf(mdlId).intValue() - 1;
         if (_modelFileNameMap != null)
         {
           int _mp = _modelFileNameMap.length - 1;
index 3b7044b..caff3bb 100644 (file)
@@ -137,13 +137,13 @@ public class Annotate3D
   public static Iterator<Reader> getRNAMLForPDBFileAsString(String pdbfile)
           throws Exception
   {
-    List<NameValuePair> vals = new ArrayList<NameValuePair>();
+    List<NameValuePair> vals = new ArrayList<>();
     vals.add(new BasicNameValuePair("tool", "rnaview"));
     vals.add(new BasicNameValuePair("data", pdbfile));
     vals.add(new BasicNameValuePair("output", "rnaml"));
     // return processJsonResponseFor(HttpClientUtils.doHttpUrlPost(twoDtoolsURL,
     // vals));
-    ArrayList<Reader> readers = new ArrayList<Reader>();
+    ArrayList<Reader> readers = new ArrayList<>();
     final BufferedReader postResponse = HttpClientUtils
             .doHttpUrlPost(twoDtoolsURL, vals, 0, 0);
     readers.add(postResponse);
@@ -164,7 +164,7 @@ public class Annotate3D
     {
       @SuppressWarnings("unchecked")
        final Iterator<Object> rvals = ((List<Object>) JSONUtils.parse(respons)).iterator();
-      return new Iterator<Reader>()
+      return new Iterator<>()
       {
         @Override
         public boolean hasNext()
@@ -186,7 +186,6 @@ public class Annotate3D
           {
             x.printStackTrace();
           }
-          ;
           if (sval == null)
           {
             System.err.println(
@@ -236,13 +235,12 @@ public class Annotate3D
               "exception.couldnt_parse_responde_from_annotated3d_server"),
               foo);
     }
-
   }
 
   public static Iterator<Reader> getRNAMLForPDBId(String pdbid)
           throws Exception
   {
-    List<NameValuePair> vals = new ArrayList<NameValuePair>();
+    List<NameValuePair> vals = new ArrayList<>();
     vals.add(new BasicNameValuePair("tool", "rnaview"));
     vals.add(new BasicNameValuePair("pdbid", pdbid));
     vals.add(new BasicNameValuePair("output", "rnaml"));
@@ -250,7 +248,7 @@ public class Annotate3D
             + pdbid + "&output=rnaml");
     // return processJsonResponseFor(new
     // InputStreamReader(geturl.openStream()));
-    ArrayList<Reader> readers = new ArrayList<Reader>();
+    ArrayList<Reader> readers = new ArrayList<>();
     readers.add(new InputStreamReader(geturl.openStream()));
     return readers.iterator();
   }
index f088a90..617d4ea 100644 (file)
@@ -43,13 +43,13 @@ import java.util.Objects;
  */
 public abstract class FTSRestClient implements FTSRestClientI
 {
-  protected Collection<FTSDataColumnI> dataColumns = new ArrayList<FTSDataColumnI>();
+  protected Collection<FTSDataColumnI> dataColumns = new ArrayList<>();
 
-  protected Collection<FTSDataColumnGroupI> dataColumnGroups = new ArrayList<FTSDataColumnGroupI>();
+  protected Collection<FTSDataColumnGroupI> dataColumnGroups = new ArrayList<>();
 
-  protected Collection<FTSDataColumnI> searchableDataColumns = new ArrayList<FTSDataColumnI>();
+  protected Collection<FTSDataColumnI> searchableDataColumns = new ArrayList<>();
 
-  protected Collection<FTSDataColumnI> defaulDisplayedDataColumns = new ArrayList<FTSDataColumnI>();
+  protected Collection<FTSDataColumnI> defaulDisplayedDataColumns = new ArrayList<>();
 
   protected FTSDataColumnI primaryKeyColumn;
 
@@ -446,22 +446,22 @@ public abstract class FTSRestClient implements FTSRestClientI
     throw new Exception("Couldn't find data column group with id : " + id);
   }
 
-  public String getMessageByHTTPStatusCode(int code, String service)
+  public static String getMessageByHTTPStatusCode(int code, String service)
   {
     String message = "";
     switch (code)
     {
     case 400:
-      message = MessageManager.getString("exception.bad_request");
+      message = "Bad request. There is a problem with your input.";
       break;
 
     case 410:
       message = MessageManager.formatMessage(
-              "exception.fts_rest_service_no_longer_available", service);
+              service + " rest services no longer available!");
       break;
     case 403:
     case 404:
-      message = MessageManager.getString("exception.resource_not_be_found");
+      message = "The requested resource could not be found";
       break;
     case 408:
     case 409:
@@ -470,16 +470,16 @@ public abstract class FTSRestClient implements FTSRestClientI
     case 502:
     case 504:
     case 505:
-      message = MessageManager.formatMessage("exception.fts_server_error",
-              service);
+      message = "There seems to be an error from the " + service
+              + " server";
       break;
     case 503:
-      message = MessageManager.getString("exception.service_not_available");
+      message = "Service not available. The server is being updated, try again later.";
       break;
     default:
       break;
     }
-    return message;
+    return String.valueOf(code) + " " + message;
   }
 
   protected String getResourceFile(String fileName)
index cd3e5c9..c52da40 100644 (file)
@@ -171,35 +171,34 @@ public class PDBFTSRestClient extends FTSRestClient
       ClientResponse clientResponse = webResource
               .accept(MediaType.APPLICATION_JSON).get(clientResponseClass );
 
-      int status = clientResponse.getStatus();
-
       // Get the JSON string from the response object or directly from the
       // client (JavaScript)
-      Map<String, Object> jsonObj = (Platform.isJS() && status == 200
-              ? clientResponse.getEntity(Map.class)
-              : null);
-      String responseString = (jsonObj == null
-              ? clientResponse.getEntity(String.class)
-              : null);
+      Map<String, Object> jsonObj = null;
+      String responseString = null;
 
       // System.out.println("query >>>>>>> " + pdbRestRequest.toString());
 
       // Check the response status and report exception if one occurs
-      switch (status)
+      int responseStatus = clientResponse.getStatus();
+      switch (responseStatus)
       {
       case 200:
+        if (Platform.isJS())
+        {
+          jsonObj = clientResponse.getEntity(Map.class);
+        }
+        else
+        {
+          responseString = clientResponse.getEntity(String.class);
+        }
         break;
       case 400:
         throw new Exception(parseJsonExceptionString(responseString));
       default:
-        throw new Exception(getMessageByHTTPStatusCode(status, "PDB"));
+        throw new Exception(
+                getMessageByHTTPStatusCode(responseStatus, "PDB"));
       }
 
-      // Make redundant objects eligible for garbage collection to conserve
-      // memory
-      clientResponse = null;
-      client = null;
-
       // Process the response and return the result to the caller.
       return parsePDBJsonResponse(responseString, jsonObj, pdbRestRequest);
     } catch (Exception e)
diff --git a/src/jalview/gui/APQHandlers.java b/src/jalview/gui/APQHandlers.java
new file mode 100644 (file)
index 0000000..c5a1b66
--- /dev/null
@@ -0,0 +1,151 @@
+package jalview.gui;
+
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
+import java.awt.Desktop;
+import java.awt.desktop.AboutEvent;
+import java.awt.desktop.AboutHandler;
+import java.awt.desktop.PreferencesEvent;
+import java.awt.desktop.PreferencesHandler;
+import java.awt.desktop.QuitEvent;
+import java.awt.desktop.QuitHandler;
+import java.awt.desktop.QuitResponse;
+import java.awt.desktop.QuitStrategy;
+
+import javax.swing.JOptionPane;
+
+public class APQHandlers
+{
+  private static boolean setAPQHandlers = false;
+
+  public APQHandlers() {
+  }
+
+  protected static boolean setAPQHandlers(jalview.gui.Desktop jalviewDesktop)
+  {
+    // flagging this test to avoid unnecessary reflection
+    if (!setAPQHandlers)
+    {
+      // see if the Quit, About and Preferences handlers are available
+      Class desktopClass = Desktop.class;
+      Desktop hdesktop = Desktop.getDesktop();
+
+      try
+      {
+        Float specversion = Float.parseFloat(
+                System.getProperty("java.specification.version"));
+
+        if (specversion >= 9)
+        {
+          if (Platform.isAMacAndNotJS())
+          {
+            if (desktopClass.getDeclaredMethod("setAboutHandler",
+                    new Class[]
+                    { AboutHandler.class }) != null)
+            {
+
+              hdesktop.setAboutHandler(new AboutHandler()
+              {
+                @Override
+                public void handleAbout(AboutEvent e)
+                {
+                  jalviewDesktop.aboutMenuItem_actionPerformed(null);
+                }
+              });
+
+            }
+
+            if (desktopClass.getDeclaredMethod("setPreferencesHandler",
+                    new Class[]
+                    { PreferencesHandler.class }) != null)
+            {
+
+              hdesktop.setPreferencesHandler(
+                      new PreferencesHandler()
+              {
+                        @Override
+                        public void handlePreferences(
+                                PreferencesEvent e)
+                        {
+                          jalviewDesktop.preferences_actionPerformed(null);
+                        }
+                      });
+
+            }
+
+            if (desktopClass.getDeclaredMethod("setQuitHandler",
+                    new Class[]
+                    { QuitHandler.class }) != null)
+            {
+
+              hdesktop.setQuitHandler(new QuitHandler()
+              {
+                @Override
+                public void handleQuitRequestWith(
+                        QuitEvent e, QuitResponse r)
+                {
+                  boolean confirmQuit = jalview.bin.Cache
+                          .getDefault(
+                                  jalview.gui.Desktop.CONFIRM_KEYBOARD_QUIT,
+                                  true);
+                  int n;
+                  if (confirmQuit)
+                  {
+                    n = JOptionPane.showConfirmDialog(null,
+                            MessageManager.getString("label.quit_jalview"),
+                            MessageManager.getString("action.quit"),
+                            JOptionPane.OK_CANCEL_OPTION,
+                            JOptionPane.PLAIN_MESSAGE, null);
+                  }
+                  else
+                  {
+                    n = JOptionPane.OK_OPTION;
+                  }
+                  if (n == JOptionPane.OK_OPTION)
+                  {
+                    System.out.println("Shortcut Quit confirmed by user");
+                    jalviewDesktop.quit();
+                    r.performQuit(); // probably won't reach this line, but just
+                                     // in
+                                     // case
+                  }
+                  else
+                  {
+                    r.cancelQuit();
+                    System.out.println("Shortcut Quit cancelled by user");
+                  }
+                }
+              });
+              hdesktop.setQuitStrategy(
+                      QuitStrategy.CLOSE_ALL_WINDOWS);
+
+            }
+          }
+          setAPQHandlers = true;
+        }
+        else
+        {
+          System.out.println(
+                  "Not going to try setting APQ Handlers as java.spec.version is "
+                          + specversion);
+        }
+
+      } catch (Exception e)
+      {
+        System.out.println(
+                "Exception when looking for About, Preferences, Quit Handlers");
+        // e.printStackTrace();
+      } catch (Throwable t)
+      {
+        System.out.println(
+                "Throwable when looking for About, Preferences, Quit Handlers");
+        // t.printStackTrace();
+      }
+
+    }
+    
+    return setAPQHandlers;
+  }
+
+}
index 27a0f76..0e9211a 100644 (file)
@@ -33,6 +33,7 @@ import jalview.api.AlignViewControllerI;
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureSettingsControllerI;
+import jalview.api.FeatureSettingsModelI;
 import jalview.api.SplitContainerI;
 import jalview.api.ViewStyleI;
 import jalview.api.analysis.SimilarityParamsI;
@@ -2158,7 +2159,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                     newGraphGroups.add(q, null);
                   }
                   newGraphGroups.set(newann.graphGroup,
-                          new Integer(++fgroup));
+                          Integer.valueOf(++fgroup));
                 }
                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
                         .intValue();
@@ -2205,7 +2206,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                     newGraphGroups.add(q, null);
                   }
                   newGraphGroups.set(newann.graphGroup,
-                          new Integer(++fgroup));
+                          Integer.valueOf(++fgroup));
                 }
                 newann.graphGroup = newGraphGroups.get(newann.graphGroup)
                         .intValue();
@@ -5044,6 +5045,13 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
               @Override
               public void finished()
               {
+
+                for (FeatureSettingsModelI srcSettings : dbRefFetcher
+                        .getFeatureSettingsModels())
+                {
+
+                  alignPanel.av.mergeFeaturesStyle(srcSettings);
+                }
                 AlignFrame.this.setMenusForViewport();
               }
             });
@@ -5120,6 +5128,10 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                                   @Override
                                   public void finished()
                                   {
+                                    FeatureSettingsModelI srcSettings = dassource[0]
+                                            .getFeatureColourScheme();
+                                    alignPanel.av.mergeFeaturesStyle(
+                                            srcSettings);
                                     AlignFrame.this.setMenusForViewport();
                                   }
                                 });
index 7d01222..91b5518 100644 (file)
@@ -47,6 +47,7 @@ import jalview.schemes.UserColourScheme;
 import jalview.structure.SelectionSource;
 import jalview.structure.StructureSelectionManager;
 import jalview.structure.VamsasSource;
+import jalview.util.ColorUtils;
 import jalview.util.MessageManager;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.AutoCalcSetting;
@@ -56,6 +57,7 @@ import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.FontMetrics;
 import java.awt.Rectangle;
+import java.util.ArrayList;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
@@ -1011,19 +1013,60 @@ public class AlignViewport extends AlignmentViewport
   @Override
   public void applyFeaturesStyle(FeatureSettingsModelI featureSettings)
   {
+    transferFeaturesStyles(featureSettings, false);
+  }
+
+  /**
+   * Applies the supplied feature settings descriptor to currently known features.
+   * This supports an 'initial configuration' of feature colouring based on a
+   * preset or user favourite. This may then be modified in the usual way using
+   * the Feature Settings dialogue.
+   * 
+   * @param featureSettings
+   */
+  @Override
+  public void mergeFeaturesStyle(FeatureSettingsModelI featureSettings)
+  {
+    transferFeaturesStyles(featureSettings, true);
+  }
+
+  /**
+   * when mergeOnly is set, then group and feature visibility or feature colours
+   * are not modified for features and groups already known to the feature
+   * renderer. Feature ordering is always adjusted, and transparency is always set
+   * regardless.
+   * 
+   * @param featureSettings
+   * @param mergeOnly
+   */
+  private void transferFeaturesStyles(FeatureSettingsModelI featureSettings,
+          boolean mergeOnly)
+  {
     if (featureSettings == null)
     {
       return;
     }
-
+    
     FeatureRenderer fr = getAlignPanel().getSeqPanel().seqCanvas
             .getFeatureRenderer();
+    List<String> origRenderOrder = new ArrayList(),
+            origGroups = new ArrayList();
+    // preserve original render order - allows differentiation between user configured colours and autogenerated ones
+    origRenderOrder.addAll(fr.getRenderOrder());
+    origGroups.addAll(fr.getFeatureGroups());
+
     fr.findAllFeatures(true);
     List<String> renderOrder = fr.getRenderOrder();
     FeaturesDisplayedI displayed = fr.getFeaturesDisplayed();
-    displayed.clear();
+    if (!mergeOnly)
+    {
+      // only clear displayed features if we are mergeing
+      displayed.clear();
+    }
     // TODO this clears displayed.featuresRegistered - do we care?
-
+    //
+    // JAL-3330 - JBP - yes we do - calling applyFeatureStyle to a view where
+    // feature visibility has already been configured is not very friendly
     /*
      * set feature colour if specified by feature settings
      * set visibility of all features
@@ -1032,13 +1075,24 @@ public class AlignViewport extends AlignmentViewport
     {
       FeatureColourI preferredColour = featureSettings
               .getFeatureColour(type);
-      if (preferredColour != null)
-      {
-        fr.setColour(type, preferredColour);
-      }
-      if (featureSettings.isFeatureDisplayed(type))
+      FeatureColourI origColour = fr.getFeatureStyle(type);
+      if (!mergeOnly || (!origRenderOrder.contains(type)
+              || origColour == null
+              || (!origColour.isGraduatedColour()
+                      && origColour.getColour() != null
+                      && origColour.getColour().equals(
+                              ColorUtils.createColourFromName(type)))))
       {
-        displayed.setVisible(type);
+        // if we are merging, only update if there wasn't already a colour defined for
+        // this type
+        if (preferredColour != null)
+        {
+          fr.setColour(type, preferredColour);
+        }
+        if (featureSettings.isFeatureDisplayed(type))
+        {
+          displayed.setVisible(type);
+        }
       }
     }
 
@@ -1047,7 +1101,12 @@ public class AlignViewport extends AlignmentViewport
      */
     for (String group : fr.getFeatureGroups())
     {
-      fr.setGroupVisibility(group, featureSettings.isGroupDisplayed(group));
+      if (!mergeOnly || !origGroups.contains(group))
+      {
+        // when merging, display groups only if the aren't already marked as not visible
+        fr.setGroupVisibility(group,
+                featureSettings.isGroupDisplayed(group));
+      }
     }
 
     /*
index 7179a18..e13df4a 100644 (file)
@@ -328,7 +328,7 @@ public class AppJmol extends StructureViewerBase
     for (String s : files)
     {
       fileList.append(SPACE).append(QUOTE)
-              .append(Platform.escapeString(s)).append(QUOTE);
+              .append(Platform.escapeBackslashes(s)).append(QUOTE);
     }
     String filesString = fileList.toString();
 
@@ -522,7 +522,7 @@ public class AppJmol extends StructureViewerBase
             addingStructures = true; // already files loaded.
             for (int c = 0; c < filesInViewer.length; c++)
             {
-              if (filesInViewer[c].equals(file))
+              if (Platform.pathEquals(filesInViewer[c], file))
               {
                 file = null;
                 break;
index c515e6c..757bb01 100644 (file)
@@ -537,7 +537,7 @@ public class BlogReader extends JPanel
     public LaunchJvBrowserOnItem(JList listItems)
     {
       super("Open in Browser");
-      this.putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
+      this.putValue(MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_O));
       this.putValue(Action.LONG_DESCRIPTION, "Open in Browser");
       _listItems = listItems;
     }
index 9167c00..c6d6e97 100644 (file)
@@ -481,7 +481,7 @@ public class ChimeraViewFrame extends StructureViewerBase
         {
           filePDB.add(thePdbEntry);
           filePDBpos.add(Integer.valueOf(pi));
-          files.append(" \"" + Platform.escapeString(file) + "\"");
+          files.append(" \"" + Platform.escapeBackslashes(file) + "\"");
         }
       }
     } catch (OutOfMemoryError oomerror)
index b5275e8..974a6e5 100644 (file)
@@ -111,6 +111,13 @@ public class CrossRefAction implements Runnable
       FeatureSettingsModelI featureColourScheme = new SequenceFetcher()
               .getFeatureColourScheme(source);
 
+      if (dna && AlignmentUtils.looksLikeEnsembl(alignment))
+      {
+        // override default featureColourScheme so products have Ensembl variant colours
+        featureColourScheme = new SequenceFetcher()
+                .getFeatureColourScheme(DBRefSource.ENSEMBL);
+      }
+
       AlignmentI xrefsAlignment = makeCrossReferencesAlignment(dataset,
               xrefs);
       if (!dna)
@@ -187,15 +194,24 @@ public class CrossRefAction implements Runnable
 
       /*
        * apply 'database source' feature configuration
-       * if any was found
+       * if any - first to the new splitframe view about to be displayed
        */
-      // TODO is this the feature colouring for the original
-      // alignment or the fetched xrefs? either could be Ensembl
+
       newFrame.getViewport().applyFeaturesStyle(featureColourScheme);
       copyThis.getViewport().applyFeaturesStyle(featureColourScheme);
 
+      /*
+       * and for JAL-3330 also to original alignFrame view(s)
+       * this currently trashes any original settings.
+       */
+      for (AlignmentViewPanel origpanel: alignFrame.getAlignPanels()) {
+        origpanel.getAlignViewport()
+                .mergeFeaturesStyle(featureColourScheme);
+      }
+
       SplitFrame sf = new SplitFrame(dna ? copyThis : newFrame,
               dna ? newFrame : copyThis);
+
       newFrame.setVisible(true);
       copyThis.setVisible(true);
       String linkedTitle = MessageManager
@@ -356,12 +372,12 @@ public class CrossRefAction implements Runnable
             seq.getLength());
     if (geneLoci != null)
     {
-      MapList map = geneLoci.getMap();
+      MapList map = geneLoci.getMapping();
       int mappedFromLength = MappingUtils.getLength(map.getFromRanges());
       if (mappedFromLength == seq.getLength())
       {
         seq.setGeneLoci(geneLoci.getSpeciesId(), geneLoci.getAssemblyId(),
-                geneLoci.getChromosomeId(), geneLoci.getMap());
+                geneLoci.getChromosomeId(), map);
         retrievedLoci.put(dbref, geneLoci);
         return true;
       }
@@ -374,12 +390,12 @@ public class CrossRefAction implements Runnable
             seq.getLength());
     if (geneLoci != null)
     {
-      MapList map = geneLoci.getMap();
+      MapList map = geneLoci.getMapping();
       int mappedFromLength = MappingUtils.getLength(map.getFromRanges());
       if (mappedFromLength == seq.getLength())
       {
         seq.setGeneLoci(geneLoci.getSpeciesId(), geneLoci.getAssemblyId(),
-                geneLoci.getChromosomeId(), geneLoci.getMap());
+                geneLoci.getChromosomeId(), map);
         retrievedLoci.put(dbref, geneLoci);
         return true;
       }
index cd8330a..5800a47 100644 (file)
@@ -20,8 +20,6 @@
  */
 package jalview.gui;
 
-import static jalview.util.UrlConstants.SEQUENCE_ID;
-
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
@@ -47,6 +45,7 @@ import jalview.util.BrowserLauncher;
 import jalview.util.ImageMaker.TYPE;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.util.ShortcutKeyMaskExWrapper;
 import jalview.util.UrlConstants;
 import jalview.viewmodel.AlignmentViewport;
 import jalview.ws.params.ParamManager;
@@ -81,12 +80,12 @@ import java.awt.event.WindowAdapter;
 import java.awt.event.WindowEvent;
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
-import java.io.BufferedInputStream;
 import java.io.File;
-import java.io.FileOutputStream;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.ListIterator;
@@ -108,7 +107,6 @@ import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JComponent;
 import javax.swing.JDesktopPane;
-import javax.swing.JFrame;
 import javax.swing.JInternalFrame;
 import javax.swing.JLabel;
 import javax.swing.JMenuItem;
@@ -122,8 +120,6 @@ import javax.swing.event.HyperlinkEvent;
 import javax.swing.event.HyperlinkEvent.EventType;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
-import javax.swing.event.MenuEvent;
-import javax.swing.event.MenuListener;
 
 import org.stackoverflowusers.file.WindowsShortcut;
 
@@ -148,6 +144,10 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
 
+  protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
+
+  public static HashMap<String, FileWriter> savingFiles = new HashMap<>();
+
   private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
 
   /**
@@ -353,22 +353,54 @@ public class Desktop extends jalview.jbgui.GDesktop
      * constructor.
      */
     instance = this;
-    if (!Platform.isJS())
+
+    doConfigureStructurePrefs();
+    setTitle("Jalview " + Cache.getProperty("VERSION"));
+    /*
+    if (!Platform.isAMac())
     {
-      doVamsasClientCheck();
+      // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
     }
+    else
+    {
+     this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+    }
+    */
 
-    doConfigureStructurePrefs();
-    setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
-    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-    boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
+    try
+    {
+      APQHandlers.setAPQHandlers(this);
+    } catch (Exception e)
+    {
+      System.out.println("Cannot set APQHandlers");
+      // e.printStackTrace();
+    } catch (Throwable t)
+    {
+      System.out.println("Cannot set APQHandlers");
+      // t.printStackTrace();
+    }
+
+
+    addWindowListener(new WindowAdapter()
+    {
+
+      @Override
+      public void windowClosing(WindowEvent ev)
+      {
+        quit();
+      }
+    });
+
+    boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
             false);
-    boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
+
+    boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
             false);
     desktop = new MyDesktopPane(selmemusage);
 
     showMemusage.setSelected(selmemusage);
     desktop.setBackground(Color.white);
+
     getContentPane().setLayout(new BorderLayout());
     // alternate config - have scrollbars - see notes in JAL-153
     // JScrollPane sp = new JScrollPane();
@@ -417,11 +449,11 @@ public class Desktop extends jalview.jbgui.GDesktop
       jconsole = new Console(this, showjconsole);
       // add essential build information
       jconsole.setHeader("Jalview Version: "
-              + jalview.bin.Cache.getProperty("VERSION") + "\n"
+              + Cache.getProperty("VERSION") + "\n"
               + "Jalview Installation: "
-              + jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
+              + Cache.getDefault("INSTALLATION", "unknown")
               + "\n" + "Build Date: "
-              + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
+              + Cache.getDefault("BUILD_DATE", "unknown") + "\n"
               + "Java version: " + System.getProperty("java.version") + "\n"
               + System.getProperty("os.arch") + " "
               + System.getProperty("os.name") + " "
@@ -475,9 +507,7 @@ public class Desktop extends jalview.jbgui.GDesktop
                           + evt.getNewValue());
                   JalviewServicesChanged(evt);
                 }
-
               });
-
     }
 
     this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
@@ -534,14 +564,14 @@ public class Desktop extends jalview.jbgui.GDesktop
     // configure services
     StructureSelectionManager ssm = StructureSelectionManager
             .getStructureSelectionManager(this);
-    if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
+    if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
     {
-      ssm.setAddTempFacAnnot(jalview.bin.Cache
+      ssm.setAddTempFacAnnot(Cache
               .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
-      ssm.setProcessSecondaryStructure(jalview.bin.Cache
+      ssm.setProcessSecondaryStructure(Cache
               .getDefault(Preferences.STRUCT_FROM_PDB, true));
       ssm.setSecStructServices(
-              jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
+              Cache.getDefault(Preferences.USE_RNAVIEW, true));
     }
     else
     {
@@ -577,10 +607,9 @@ public class Desktop extends jalview.jbgui.GDesktop
       public void run()
       {
         Cache.log.debug("Downloading data from identifiers.org");
-        UrlDownloadClient client = new UrlDownloadClient();
         try
         {
-          client.download(IdOrgSettings.getUrl(),
+          UrlDownloadClient.download(IdOrgSettings.getUrl(),
                   IdOrgSettings.getDownloadLocation());
         } catch (IOException e)
         {
@@ -589,7 +618,7 @@ public class Desktop extends jalview.jbgui.GDesktop
         }
       }
     }).start();
-    ;
+    
   }
 
   @Override
@@ -632,25 +661,25 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     // TODO: lock aspect ratio for scaling desktop Bug #0058199
     Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
-    String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
-    String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
-    String width = jalview.bin.Cache
+    String x = Cache.getProperty(windowName + "SCREEN_X");
+    String y = Cache.getProperty(windowName + "SCREEN_Y");
+    String width = Cache
             .getProperty(windowName + "SCREEN_WIDTH");
-    String height = jalview.bin.Cache
+    String height = Cache
             .getProperty(windowName + "SCREEN_HEIGHT");
     if ((x != null) && (y != null) && (width != null) && (height != null))
     {
       int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
               iw = Integer.parseInt(width), ih = Integer.parseInt(height);
-      if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
+      if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
       {
         // attempt #1 - try to cope with change in screen geometry - this
         // version doesn't preserve original jv aspect ratio.
         // take ratio of current screen size vs original screen size.
         double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
-                jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
+                Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
         double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
-                jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
+                Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
         // rescale the bounds depending upon the current screen geometry.
         ix = (int) (ix * sw);
         iw = (int) (iw * sw);
@@ -658,17 +687,17 @@ public class Desktop extends jalview.jbgui.GDesktop
         ih = (int) (ih * sh);
         while (ix >= screenSize.width)
         {
-          jalview.bin.Cache.log.debug(
+          Cache.log.debug(
                   "Window geometry location recall error: shifting horizontal to within screenbounds.");
           ix -= screenSize.width;
         }
         while (iy >= screenSize.height)
         {
-          jalview.bin.Cache.log.debug(
+          Cache.log.debug(
                   "Window geometry location recall error: shifting vertical to within screenbounds.");
           iy -= screenSize.height;
         }
-        jalview.bin.Cache.log.debug(
+        Cache.log.debug(
                 "Got last known dimensions for " + windowName + ": x:" + ix
                         + " y:" + iy + " width:" + iw + " height:" + ih);
       }
@@ -678,46 +707,6 @@ public class Desktop extends jalview.jbgui.GDesktop
     return null;
   }
 
-  private void doVamsasClientCheck()
-  {
-    if (Cache.vamsasJarsPresent())
-    {
-      setupVamsasDisconnectedGui();
-      VamsasMenu.setVisible(true);
-      final Desktop us = this;
-      VamsasMenu.addMenuListener(new MenuListener()
-      {
-        // this listener remembers when the menu was first selected, and
-        // doesn't rebuild the session list until it has been cleared and
-        // reselected again.
-        boolean refresh = true;
-
-        @Override
-        public void menuCanceled(MenuEvent e)
-        {
-          refresh = true;
-        }
-
-        @Override
-        public void menuDeselected(MenuEvent e)
-        {
-          refresh = true;
-        }
-
-        @Override
-        public void menuSelected(MenuEvent e)
-        {
-          if (refresh)
-          {
-            us.buildVamsasStMenu();
-            refresh = false;
-          }
-        }
-      });
-      vamsasStart.setVisible(true);
-    }
-  }
-
   void showPasteMenu(int x, int y)
   {
     JPopupMenu popup = new JPopupMenu();
@@ -940,7 +929,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           menuItem.removeActionListener(menuItem.getActionListeners()[0]);
         }
         windowMenu.remove(menuItem);
-      };
+      }
     });
 
     menuItem.addActionListener(new ActionListener()
@@ -1004,7 +993,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
             InputEvent.CTRL_DOWN_MASK);
     KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
+            ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
 
     InputMap inputMap = frame
             .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
@@ -1123,7 +1112,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
     JalviewFileChooser chooser = JalviewFileChooser
-            .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, true);
+            .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
 
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
@@ -1309,9 +1298,9 @@ public class Desktop extends jalview.jbgui.GDesktop
   public void quit()
   {
     Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
-    jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
+    Cache.setProperty("SCREENGEOMETRY_WIDTH",
             screen.width + "");
-    jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT",
+    Cache.setProperty("SCREENGEOMETRY_HEIGHT",
             screen.height + "");
     storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
             getWidth(), getHeight()));
@@ -1343,14 +1332,14 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   private void storeLastKnownDimensions(String string, Rectangle jc)
   {
-    jalview.bin.Cache.log.debug("Storing last known dimensions for "
+    Cache.log.debug("Storing last known dimensions for "
             + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
             + " height:" + jc.height);
 
-    jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
-    jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
-    jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
-    jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
+    Cache.setProperty(string + "SCREEN_X", jc.x + "");
+    Cache.setProperty(string + "SCREEN_Y", jc.y + "");
+    Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
+    Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
   }
 
   /**
@@ -1383,32 +1372,34 @@ public class Desktop extends jalview.jbgui.GDesktop
     if (shortv)
     {
       message.append("<h1><strong>Version: "
-              + jalview.bin.Cache.getProperty("VERSION")
+              + Cache.getProperty("VERSION")
               + "</strong></h1>");
-      message.append("<strong>Last Updated: <em>"
-              + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
-              + "</em></strong>");
+      message.append("<strong>Built: <em>"
+              + Cache.getDefault("BUILD_DATE", "unknown") + "</em> from "
+              + Cache.getBuildDetailsForSplash()
+              + "</strong>");
 
     }
     else
     {
 
       message.append("<strong>Version "
-              + jalview.bin.Cache.getProperty("VERSION")
+              + Cache.getProperty("VERSION")
               + "; last updated: "
-              + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
+              + Cache.getDefault("BUILD_DATE", "unknown"));
     }
 
-    if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
+    if (Cache.getDefault("LATEST_VERSION", "Checking")
             .equals("Checking"))
     {
-      message.append("<br>...Checking latest version...</br>");
+      // JBP removed this message for 2.11: May be reinstated in future version
+      // message.append("<br>...Checking latest version...</br>");
     }
-    else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
-            .equals(jalview.bin.Cache.getProperty("VERSION")))
+    else if (!Cache.getDefault("LATEST_VERSION", "Checking")
+            .equals(Cache.getProperty("VERSION")))
     {
       boolean red = false;
-      if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
+      if (Cache.getProperty("VERSION").toLowerCase()
               .indexOf("automated build") == -1)
       {
         red = true;
@@ -1418,10 +1409,10 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
 
       message.append("<br>!! Version "
-              + jalview.bin.Cache.getDefault("LATEST_VERSION",
+              + Cache.getDefault("LATEST_VERSION",
                       "..Checking..")
               + " is available for download from "
-              + jalview.bin.Cache.getDefault("www.jalview.org",
+              + Cache.getDefault("www.jalview.org",
                       "http://www.jalview.org")
               + " !!");
       if (red)
@@ -1429,7 +1420,7 @@ public class Desktop extends jalview.jbgui.GDesktop
         message.append("</div>");
       }
     }
-    message.append("<br>Authors:  " + jalview.bin.Cache.getDefault(
+    message.append("<br>Authors:  " + Cache.getDefault(
             "AUTHORFNAMES",
             "The Jalview Authors (See AUTHORS file for current list)")
             + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
@@ -1485,10 +1476,6 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
     Jalview.setCurrentAlignFrame(null);
     System.out.println("ALL CLOSED");
-    if (v_client != null)
-    {
-      // TODO clear binding to vamsas document objects on close_all
-    }
 
     /*
      * reset state of singleton objects as appropriate (clear down session state
@@ -1529,9 +1516,9 @@ public class Desktop extends jalview.jbgui.GDesktop
   protected void garbageCollect_actionPerformed(ActionEvent e)
   {
     // We simply collect the garbage
-    jalview.bin.Cache.log.debug("Collecting garbage...");
+    Cache.log.debug("Collecting garbage...");
     System.gc();
-    jalview.bin.Cache.log.debug("Finished garbage collection.");
+    Cache.log.debug("Finished garbage collection.");
   }
 
   /*
@@ -1721,7 +1708,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           setProgressBar(MessageManager.formatMessage(
                   "label.saving_jalview_project", new Object[]
                   { chosenFile.getName() }), chosenFile.hashCode());
-          jalview.bin.Cache.setProperty("LAST_DIRECTORY",
+          Cache.setProperty("LAST_DIRECTORY",
                   chosenFile.getParent());
           // TODO catch and handle errors for savestate
           // TODO prevent user from messing with the Desktop whilst we're saving
@@ -1787,7 +1774,7 @@ public class Desktop extends jalview.jbgui.GDesktop
         "Jalview Project (old)" };
     JalviewFileChooser chooser = new JalviewFileChooser(
             Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
-            "Jalview Project", true, true); // last two booleans: allFiles,
+            "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
                                             // allowBackupFiles
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
@@ -2081,369 +2068,6 @@ public class Desktop extends jalview.jbgui.GDesktop
         }
       }
     }
-
-  }
-
-  jalview.gui.VamsasApplication v_client = null;
-
-  @Override
-  public void vamsasImport_actionPerformed(ActionEvent e)
-  {
-    // TODO: JAL-3048 not needed for Jalview-JS
-
-    if (v_client == null)
-    {
-      // Load and try to start a session.
-      JalviewFileChooser chooser = new JalviewFileChooser(
-              jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
-
-      chooser.setFileView(new JalviewFileView());
-      chooser.setDialogTitle(
-              MessageManager.getString("label.open_saved_vamsas_session"));
-      chooser.setToolTipText(MessageManager.getString(
-              "label.select_vamsas_session_opened_as_new_vamsas_session"));
-
-      int value = chooser.showOpenDialog(this);
-
-      if (value == JalviewFileChooser.APPROVE_OPTION)
-      {
-        String fle = chooser.getSelectedFile().toString();
-        if (!vamsasImport(chooser.getSelectedFile()))
-        {
-          JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-                  MessageManager.formatMessage(
-                          "label.couldnt_import_as_vamsas_session",
-                          new Object[]
-                          { fle }),
-                  MessageManager
-                          .getString("label.vamsas_document_import_failed"),
-                  JvOptionPane.ERROR_MESSAGE);
-        }
-      }
-    }
-    else
-    {
-      jalview.bin.Cache.log.error(
-              "Implementation error - load session from a running session is not supported.");
-    }
-  }
-
-  /**
-   * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
-   * 
-   * @param file
-   * @return true if import was a success and a session was started.
-   */
-  public boolean vamsasImport(URL url)
-  {
-    // TODO: create progress bar
-    if (v_client != null)
-    {
-
-      jalview.bin.Cache.log.error(
-              "Implementation error - load session from a running session is not supported.");
-      return false;
-    }
-
-    try
-    {
-      // copy the URL content to a temporary local file
-      // TODO: be a bit cleverer here with nio (?!)
-      File file = File.createTempFile("vdocfromurl", ".vdj");
-      FileOutputStream fos = new FileOutputStream(file);
-      BufferedInputStream bis = new BufferedInputStream(url.openStream());
-      byte[] buffer = new byte[2048];
-      int ln;
-      while ((ln = bis.read(buffer)) > -1)
-      {
-        fos.write(buffer, 0, ln);
-      }
-      bis.close();
-      fos.close();
-      v_client = new jalview.gui.VamsasApplication(this, file,
-              url.toExternalForm());
-    } catch (Exception ex)
-    {
-      jalview.bin.Cache.log.error(
-              "Failed to create new vamsas session from contents of URL "
-                      + url,
-              ex);
-      return false;
-    }
-    setupVamsasConnectedGui();
-    v_client.initial_update(); // TODO: thread ?
-    return v_client.inSession();
-  }
-
-  /**
-   * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
-   * 
-   * @param file
-   * @return true if import was a success and a session was started.
-   */
-  public boolean vamsasImport(File file)
-  {
-    if (v_client != null)
-    {
-
-      jalview.bin.Cache.log.error(
-              "Implementation error - load session from a running session is not supported.");
-      return false;
-    }
-
-    setProgressBar(MessageManager.formatMessage(
-            "status.importing_vamsas_session_from", new Object[]
-            { file.getName() }), file.hashCode());
-    try
-    {
-      v_client = new jalview.gui.VamsasApplication(this, file, null);
-    } catch (Exception ex)
-    {
-      setProgressBar(MessageManager.formatMessage(
-              "status.importing_vamsas_session_from", new Object[]
-              { file.getName() }), file.hashCode());
-      jalview.bin.Cache.log.error(
-              "New vamsas session from existing session file failed:", ex);
-      return false;
-    }
-    setupVamsasConnectedGui();
-    v_client.initial_update(); // TODO: thread ?
-    setProgressBar(MessageManager.formatMessage(
-            "status.importing_vamsas_session_from", new Object[]
-            { file.getName() }), file.hashCode());
-    return v_client.inSession();
-  }
-
-  public boolean joinVamsasSession(String mysesid)
-  {
-    if (v_client != null)
-    {
-      throw new Error(MessageManager
-              .getString("error.try_join_vamsas_session_another"));
-    }
-    if (mysesid == null)
-    {
-      throw new Error(
-              MessageManager.getString("error.invalid_vamsas_session_id"));
-    }
-    v_client = new VamsasApplication(this, mysesid);
-    setupVamsasConnectedGui();
-    v_client.initial_update();
-    return (v_client.inSession());
-  }
-
-  @Override
-  public void vamsasStart_actionPerformed(ActionEvent e)
-  {
-    if (v_client == null)
-    {
-      // Start a session.
-      // we just start a default session for moment.
-      /*
-       * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
-       * getProperty("LAST_DIRECTORY"));
-       * 
-       * chooser.setFileView(new JalviewFileView());
-       * chooser.setDialogTitle("Load Vamsas file");
-       * chooser.setToolTipText("Import");
-       * 
-       * int value = chooser.showOpenDialog(this);
-       * 
-       * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
-       * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
-       */
-      v_client = new VamsasApplication(this);
-      setupVamsasConnectedGui();
-      v_client.initial_update(); // TODO: thread ?
-    }
-    else
-    {
-      // store current data in session.
-      v_client.push_update(); // TODO: thread
-    }
-  }
-
-  protected void setupVamsasConnectedGui()
-  {
-    vamsasStart.setText(MessageManager.getString("label.session_update"));
-    vamsasSave.setVisible(true);
-    vamsasStop.setVisible(true);
-    vamsasImport.setVisible(false); // Document import to existing session is
-    // not possible for vamsas-client-1.0.
-  }
-
-  protected void setupVamsasDisconnectedGui()
-  {
-    vamsasSave.setVisible(false);
-    vamsasStop.setVisible(false);
-    vamsasImport.setVisible(true);
-    vamsasStart
-            .setText(MessageManager.getString("label.new_vamsas_session"));
-  }
-
-  @Override
-  public void vamsasStop_actionPerformed(ActionEvent e)
-  {
-    if (v_client != null)
-    {
-      v_client.end_session();
-      v_client = null;
-      setupVamsasDisconnectedGui();
-    }
-  }
-
-  protected void buildVamsasStMenu()
-  {
-    if (v_client == null)
-    {
-      String[] sess = null;
-      try
-      {
-        sess = VamsasApplication.getSessionList();
-      } catch (Exception e)
-      {
-        jalview.bin.Cache.log.warn("Problem getting current sessions list.",
-                e);
-        sess = null;
-      }
-      if (sess != null)
-      {
-        jalview.bin.Cache.log.debug(
-                "Got current sessions list: " + sess.length + " entries.");
-        VamsasStMenu.removeAll();
-        for (int i = 0; i < sess.length; i++)
-        {
-          JMenuItem sessit = new JMenuItem();
-          sessit.setText(sess[i]);
-          sessit.setToolTipText(MessageManager
-                  .formatMessage("label.connect_to_session", new Object[]
-                  { sess[i] }));
-          final Desktop dsktp = this;
-          final String mysesid = sess[i];
-          sessit.addActionListener(new ActionListener()
-          {
-
-            @Override
-            public void actionPerformed(ActionEvent e)
-            {
-              if (dsktp.v_client == null)
-              {
-                Thread rthr = new Thread(new Runnable()
-                {
-
-                  @Override
-                  public void run()
-                  {
-                    dsktp.v_client = new VamsasApplication(dsktp, mysesid);
-                    dsktp.setupVamsasConnectedGui();
-                    dsktp.v_client.initial_update();
-                  }
-
-                });
-                rthr.start();
-              }
-            };
-          });
-          VamsasStMenu.add(sessit);
-        }
-        // don't show an empty menu.
-        VamsasStMenu.setVisible(sess.length > 0);
-
-      }
-      else
-      {
-        jalview.bin.Cache.log.debug("No current vamsas sessions.");
-        VamsasStMenu.removeAll();
-        VamsasStMenu.setVisible(false);
-      }
-    }
-    else
-    {
-      // Not interested in the content. Just hide ourselves.
-      VamsasStMenu.setVisible(false);
-    }
-  }
-
-  @Override
-  public void vamsasSave_actionPerformed(ActionEvent e)
-  {
-    // TODO: JAL-3048 not needed for Jalview-JS
-
-    if (v_client != null)
-    {
-      // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
-      JalviewFileChooser chooser = new JalviewFileChooser("vdj",
-              "Vamsas Document");
-
-      chooser.setFileView(new JalviewFileView());
-      chooser.setDialogTitle(MessageManager
-              .getString("label.save_vamsas_document_archive"));
-
-      int value = chooser.showSaveDialog(this);
-
-      if (value == JalviewFileChooser.APPROVE_OPTION)
-      {
-        java.io.File choice = chooser.getSelectedFile();
-        JPanel progpanel = addProgressPanel(MessageManager
-                .formatMessage("label.saving_vamsas_doc", new Object[]
-                { choice.getName() }));
-        Cache.setProperty("LAST_DIRECTORY", choice.getParent());
-        String warnmsg = null;
-        String warnttl = null;
-        try
-        {
-          v_client.vclient.storeDocument(choice);
-        } catch (Error ex)
-        {
-          warnttl = "Serious Problem saving Vamsas Document";
-          warnmsg = ex.toString();
-          jalview.bin.Cache.log
-                  .error("Error Whilst saving document to " + choice, ex);
-
-        } catch (Exception ex)
-        {
-          warnttl = "Problem saving Vamsas Document.";
-          warnmsg = ex.toString();
-          jalview.bin.Cache.log.warn(
-                  "Exception Whilst saving document to " + choice, ex);
-
-        }
-        removeProgressPanel(progpanel);
-        if (warnmsg != null)
-        {
-          JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-
-                  warnmsg, warnttl, JvOptionPane.ERROR_MESSAGE);
-        }
-      }
-    }
-  }
-
-  JPanel vamUpdate = null;
-
-  /**
-   * hide vamsas user gui bits when a vamsas document event is being handled.
-   * 
-   * @param b
-   *          true to hide gui, false to reveal gui
-   */
-  public void setVamsasUpdate(boolean b)
-  {
-    Cache.log.debug("Setting gui for Vamsas update "
-            + (b ? "in progress" : "finished"));
-
-    if (vamUpdate != null)
-    {
-      this.removeProgressPanel(vamUpdate);
-    }
-    if (b)
-    {
-      vamUpdate = this.addProgressPanel(
-              MessageManager.getString("label.updating_vamsas_session"));
-    }
-    vamsasStart.setVisible(!b);
-    vamsasStop.setVisible(!b);
-    vamsasSave.setVisible(!b);
   }
 
   public JInternalFrame[] getAllFrames()
@@ -2487,7 +2111,7 @@ public class Desktop extends jalview.jbgui.GDesktop
           while (li.hasNext())
           {
             String link = li.next();
-            if (link.contains(SEQUENCE_ID)
+            if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
                     && !UrlConstants.isDefaultString(link))
             {
               check = true;
@@ -2733,7 +2357,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       openGroovyConsole();
     } catch (Exception ex)
     {
-      jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
+      Cache.log.error("Groovy Shell Creation failed.", ex);
       JvOptionPane.showInternalMessageDialog(Desktop.desktop,
 
               MessageManager.getString("label.couldnt_create_groovy_shell"),
@@ -2794,7 +2418,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   {
     getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
             .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
-                    Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
+                    jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
                     "Quit");
     getRootPane().getActionMap().put("Quit", new AbstractAction()
     {
@@ -2853,18 +2477,18 @@ public class Desktop extends jalview.jbgui.GDesktop
       progressBarHandlers = new Hashtable<>();
     }
 
-    if (progressBars.get(new Long(id)) != null)
+    if (progressBars.get(Long.valueOf(id)) != null)
     {
-      JPanel panel = progressBars.remove(new Long(id));
-      if (progressBarHandlers.contains(new Long(id)))
+      JPanel panel = progressBars.remove(Long.valueOf(id));
+      if (progressBarHandlers.contains(Long.valueOf(id)))
       {
-        progressBarHandlers.remove(new Long(id));
+        progressBarHandlers.remove(Long.valueOf(id));
       }
       removeProgressPanel(panel);
     }
     else
     {
-      progressBars.put(new Long(id), addProgressPanel(message));
+      progressBars.put(Long.valueOf(id), addProgressPanel(message));
     }
   }
 
@@ -2879,13 +2503,13 @@ public class Desktop extends jalview.jbgui.GDesktop
           final IProgressIndicatorHandler handler)
   {
     if (progressBarHandlers == null
-            || !progressBars.containsKey(new Long(id)))
+            || !progressBars.containsKey(Long.valueOf(id)))
     {
       throw new Error(MessageManager.getString(
               "error.call_setprogressbar_before_registering_handler"));
     }
-    progressBarHandlers.put(new Long(id), handler);
-    final JPanel progressPanel = progressBars.get(new Long(id));
+    progressBarHandlers.put(Long.valueOf(id), handler);
+    final JPanel progressPanel = progressBars.get(Long.valueOf(id));
     if (handler.canCancel())
     {
       JButton cancel = new JButton(
@@ -2948,7 +2572,8 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   public VamsasApplication getVamsasApplication()
   {
-    return v_client;
+    // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
+    return null;
 
   }
 
@@ -3239,7 +2864,6 @@ public class Desktop extends jalview.jbgui.GDesktop
           } catch (InterruptedException x)
           {
           }
-          ;
         }
         if (instance == null)
         {
@@ -3527,7 +3151,6 @@ public class Desktop extends jalview.jbgui.GDesktop
             System.err.println(
                     "Please ignore plist error - occurs due to problem with java 8 on OSX");
           }
-          ;
         }
       } catch (Throwable ex)
       {
index 0553fef..83badd0 100644 (file)
  */
 package jalview.gui;
 
-import java.awt.Color;
-
 /**
- * DOCUMENT ME!
- * 
- * @author $author$
- * @version $Revision$
+ * A class that manages drawing of sequence features for the Swing gui
  */
 public class FeatureRenderer
         extends jalview.renderer.seqfeatures.FeatureRenderer
 {
-  Color resBoxColour;
-
   AlignmentPanel ap;
 
   /**
index f81ecce..9fe2e92 100644 (file)
@@ -705,7 +705,7 @@ public class FeatureSettings extends JPanel
         data[dataIndex][FILTER_COLUMN] = featureFilter == null
                 ? new FeatureMatcherSet()
                 : featureFilter;
-        data[dataIndex][SHOW_COLUMN] = new Boolean(
+        data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
                 af.getViewport().getFeaturesDisplayed().isVisible(type));
         dataIndex++;
         displayableTypes.remove(type);
@@ -732,7 +732,7 @@ public class FeatureSettings extends JPanel
       data[dataIndex][FILTER_COLUMN] = featureFilter == null
               ? new FeatureMatcherSet()
               : featureFilter;
-      data[dataIndex][SHOW_COLUMN] = new Boolean(true);
+      data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
       dataIndex++;
       displayableTypes.remove(type);
     }
index 735e514..bac9d9b 100644 (file)
@@ -22,6 +22,7 @@ package jalview.gui;
 
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureColourI;
+import jalview.bin.Cache;
 import jalview.datamodel.GraphLine;
 import jalview.datamodel.features.FeatureAttributes;
 import jalview.datamodel.features.FeatureAttributes.Datatype;
@@ -164,7 +165,7 @@ public class FeatureTypeSettings extends JalviewDialog
 
   JPanel maxColour = new JPanel();
 
-  private JComboBox<String> threshold = new JComboBox<>();
+  private JComboBox<Object> threshold = new JComboBox<>();
 
   JSlider slider = new JSlider();
 
@@ -181,17 +182,17 @@ public class FeatureTypeSettings extends JalviewDialog
   /*
    * choice of option for 'colour for no value'
    */
-  private JComboBox<String> noValueCombo;
+  private JComboBox<Object> noValueCombo;
 
   /*
    * choice of what to colour by text (Label or attribute)
    */
-  private JComboBox<String> colourByTextCombo;
+  private JComboBox<Object> colourByTextCombo;
 
   /*
    * choice of what to colour by range (Score or attribute)
    */
-  private JComboBox<String> colourByRangeCombo;
+  private JComboBox<Object> colourByRangeCombo;
 
   private JRadioButton andFilters;
 
@@ -1121,7 +1122,7 @@ public class FeatureTypeSettings extends JalviewDialog
    * @param withRange
    * @param withText
    */
-  protected JComboBox<String> populateAttributesDropdown(
+  protected JComboBox<Object> populateAttributesDropdown(
           List<String[]> attNames, boolean withRange, boolean withText)
   {
     List<String> displayAtts = new ArrayList<>();
@@ -1160,9 +1161,11 @@ public class FeatureTypeSettings extends JalviewDialog
       tooltips.add(desc == null ? "" : desc);
     }
 
-    JComboBox<String> attCombo = JvSwingUtils
-            .buildComboWithTooltips(displayAtts, tooltips);
-
+    // now convert String List to Object List for buildComboWithTooltips
+    List<Object> displayAttsObjects = new ArrayList<>(displayAtts);
+    JComboBox<Object> attCombo = JvSwingUtils
+            .buildComboWithTooltips(displayAttsObjects, tooltips);
+    
     return attCombo;
   }
 
@@ -1340,8 +1343,8 @@ public class FeatureTypeSettings extends JalviewDialog
      * drop-down choice of attribute, with description as a tooltip 
      * if we can obtain it
      */
-    JComboBox<String> attCombo = populateAttributesDropdown(attNames, true,
-            true);
+    final JComboBox<Object> attCombo = populateAttributesDropdown(attNames,
+            true, true);
     String filterBy = setSelectedAttribute(attCombo, filter);
 
     JComboBox<Condition> condCombo = new JComboBox<>();
@@ -1442,12 +1445,13 @@ public class FeatureTypeSettings extends JalviewDialog
     if (!patternField.isEnabled()
             || (pattern != null && pattern.trim().length() > 0))
     {
-      JButton removeCondition = new JButton("\u2717"); // Dingbats cursive x
+      JButton removeCondition = new JButton("\u2717");
+      // Dingbats cursive x
       removeCondition.setBorder(new EmptyBorder(0, 0, 0, 0));
       removeCondition.setBackground(Color.WHITE);
       removeCondition.setPreferredSize(new Dimension(23, 17));
-      removeCondition
-              .setToolTipText(MessageManager.getString("label.delete_row"));
+      removeCondition.setToolTipText(
+              MessageManager.getString("label.delete_condition"));
       removeCondition.addActionListener(new ActionListener()
       {
         @Override
@@ -1470,7 +1474,7 @@ public class FeatureTypeSettings extends JalviewDialog
    * @param attCombo
    * @param filter
    */
-  private String setSelectedAttribute(JComboBox<String> attCombo,
+  private String setSelectedAttribute(JComboBox<Object> attCombo,
           FeatureMatcherI filter)
   {
     String item = null;
@@ -1687,11 +1691,19 @@ public class FeatureTypeSettings extends JalviewDialog
    * @param valueField
    * @param filterIndex
    */
-  protected boolean updateFilter(JComboBox<String> attCombo,
+  protected boolean updateFilter(JComboBox<Object> attCombo,
           JComboBox<Condition> condCombo, JTextField valueField,
           int filterIndex)
   {
-    String attName = (String) attCombo.getSelectedItem();
+    String attName;
+    try
+    {
+      attName = (String) attCombo.getSelectedItem();
+    } catch (Exception e)
+    {
+      Cache.log.error("Problem casting Combo box entry to String");
+      attName = attCombo.getSelectedItem().toString();
+    }
     Condition cond = (Condition) condCombo.getSelectedItem();
     String pattern = valueField.getText().trim();
 
index d44080a..41287d2 100644 (file)
@@ -330,13 +330,13 @@ public final class JvSwingUtils
    * @param entries
    * @param tooltips
    */
-  public static JComboBox<String> buildComboWithTooltips(
-          List<String> entries, List<String> tooltips)
+  public static JComboBox<Object> buildComboWithTooltips(
+          List<Object> entries, List<String> tooltips)
   {
-    JComboBox<String> combo = new JComboBox<>();
+    JComboBox<Object> combo = new JComboBox<>();
     final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer();
     combo.setRenderer(renderer);
-    for (String attName : entries)
+    for (Object attName : entries)
     {
       combo.addItem(attName);
     }
index d1e7565..96a8b0d 100644 (file)
@@ -572,11 +572,11 @@ public class PCAPanel extends GPCAPanel
     // }
     //
     // JPanel progressPanel;
-    // Long lId = new Long(id);
+    // Long lId = Long.valueOf(id);
     // GridLayout layout = (GridLayout) statusPanel.getLayout();
     // if (progressBars.get(lId) != null)
     // {
-    // progressPanel = (JPanel) progressBars.get(new Long(id));
+    // progressPanel = (JPanel) progressBars.get(Long.valueOf(id));
     // statusPanel.remove(progressPanel);
     // progressBars.remove(lId);
     // progressPanel = null;
@@ -615,13 +615,13 @@ public class PCAPanel extends GPCAPanel
           final IProgressIndicatorHandler handler)
   {
     progressBar.registerHandler(id, handler);
-    // if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
+    // if (progressBarHandlers == null || !progressBars.contains(Long.valueOf(id)))
     // {
     // throw new
     // Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
     // }
-    // progressBarHandlers.put(new Long(id), handler);
-    // final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
+    // progressBarHandlers.put(Long.valueOf(id), handler);
+    // final JPanel progressPanel = (JPanel) progressBars.get(Long.valueOf(id));
     // if (handler.canCancel())
     // {
     // JButton cancel = new JButton(
index 9d5596e..9f05c2b 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.bin.Cache;
 import jalview.gui.Help.HelpId;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.BackupFiles;
+import jalview.io.BackupFilesPresetEntry;
 import jalview.io.FileFormatI;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
@@ -839,22 +840,25 @@ public class Preferences extends GPreferences
     /*
      * Save Backups settings
      */
-    Cache.applicationProperties.setProperty(BackupFiles.CONFIRM_DELETE_OLD,
-            Boolean.toString(backupfilesConfirmDelete.isSelected()));
     Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
             Boolean.toString(enableBackupFiles.isSelected()));
-    Cache.applicationProperties.setProperty(BackupFiles.NO_MAX,
-            Boolean.toString(backupfilesKeepAll.isSelected()));
-    Cache.applicationProperties.setProperty(BackupFiles.REVERSE_ORDER,
-            Boolean.toString(suffixReverse.isSelected()));
-    Cache.applicationProperties.setProperty(BackupFiles.SUFFIX,
-            suffixTemplate.getText());
-    Cache.applicationProperties.setProperty(BackupFiles.ROLL_MAX,
-            Integer.toString(getSpinnerInt(backupfilesRollMaxSpinner, 4)));
-    Cache.applicationProperties.setProperty(BackupFiles.SUFFIX_DIGITS,
-            Integer.toString(getSpinnerInt(suffixDigitsSpinner, 3)));
-    Cache.applicationProperties.setProperty(BackupFiles.NS+"_PRESET",
-            Integer.toString(getComboIntStringKey(backupfilesPresetsCombo)));
+    int preset = getComboIntStringKey(backupfilesPresetsCombo);
+    Cache.applicationProperties.setProperty(BackupFiles.NS + "_PRESET", Integer.toString(preset));
+
+    if (preset == BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM)
+    {
+      BackupFilesPresetEntry customBFPE = getBackupfilesCurrentEntry();
+      BackupFilesPresetEntry.backupfilesPresetEntriesValues.put(
+              BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM, customBFPE);
+      Cache.applicationProperties
+              .setProperty(BackupFilesPresetEntry.CUSTOMCONFIG,
+                      customBFPE.toString());
+    }
+
+    BackupFilesPresetEntry savedBFPE = BackupFilesPresetEntry.backupfilesPresetEntriesValues
+            .get(preset);
+    Cache.applicationProperties.setProperty(
+            BackupFilesPresetEntry.SAVEDCONFIG, savedBFPE.toString());
 
     Cache.saveProperties();
     Desktop.instance.doConfigureStructurePrefs();
index dc0cdb4..561fb3c 100755 (executable)
@@ -630,7 +630,7 @@ public class RotatableCanvas extends JPanel implements MouseListener,
     int yDelta = yPos - mouseY;
 
     // Check if this is a rectangle drawing drag
-    if ((evt.getModifiers() & InputEvent.BUTTON2_MASK) != 0)
+    if ((evt.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK) != 0)
     {
       // rectx2 = evt.getX();
       // recty2 = evt.getY();
index 81b394b..fb967ed 100755 (executable)
@@ -30,6 +30,9 @@ import java.awt.Color;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
 
+import org.jfree.graphics2d.svg.SVGGraphics2D;
+import org.jibble.epsgraphics.EpsGraphics2D;
+
 public class SequenceRenderer implements jalview.api.SequenceRenderer
 {
   final static int CHAR_TO_UPPER = 'A' - 'a';
@@ -254,8 +257,19 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer
     }
     graphics.setColor(av.getTextColour());
 
-    if (monospacedFont && av.getShowText() && allGroups.length == 0
-            && !av.getColourText() && av.getThresholdTextColour() == 0)
+    boolean drawAllText = monospacedFont && av.getShowText() && allGroups.length == 0
+            && !av.getColourText() && av.getThresholdTextColour() == 0;
+
+    /*
+     * EPS or SVG misaligns monospaced strings (JAL-3239)
+     * so always draw these one character at a time
+     */
+    if (graphics instanceof EpsGraphics2D
+            || graphics instanceof SVGGraphics2D)
+    {
+      drawAllText = false;
+    }
+    if (drawAllText)
     {
       if (av.isRenderGaps())
       {
index e066c75..9d521b4 100644 (file)
@@ -29,7 +29,6 @@ import jalview.util.Platform;
 import jalview.viewmodel.AlignmentViewport;
 
 import java.awt.Component;
-import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.KeyAdapter;
@@ -43,6 +42,8 @@ import java.util.Map.Entry;
 import javax.swing.AbstractAction;
 import javax.swing.InputMap;
 import javax.swing.JComponent;
+import javax.swing.JDesktopPane;
+import javax.swing.JInternalFrame;
 import javax.swing.JMenuItem;
 import javax.swing.KeyStroke;
 import javax.swing.event.InternalFrameAdapter;
@@ -412,7 +413,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * Ctrl-W / Cmd-W - close view or window
      */
     KeyStroke key_cmdW = KeyStroke.getKeyStroke(KeyEvent.VK_W,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     action = new AbstractAction()
     {
       @Override
@@ -433,7 +434,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * Ctrl-T / Cmd-T open new view
      */
     KeyStroke key_cmdT = KeyStroke.getKeyStroke(KeyEvent.VK_T,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     AbstractAction action = new AbstractAction()
     {
       @Override
@@ -773,7 +774,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
      * Ctrl-F / Cmd-F open Finder dialog, 'focused' on the right alignment
      */
     KeyStroke key_cmdF = KeyStroke.getKeyStroke(KeyEvent.VK_F,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     AbstractAction action = new AbstractAction()
     {
       @Override
@@ -789,4 +790,29 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
     };
     overrideKeyBinding(key_cmdF, action);
   }
+
+  /**
+   * Override to do nothing if triggered from one of the child frames
+   */
+  @Override
+  public void setSelected(boolean selected) throws PropertyVetoException
+  {
+    JDesktopPane desktopPane = getDesktopPane();
+    JInternalFrame fr = desktopPane == null ? null
+            : desktopPane.getSelectedFrame();
+    if (fr == getTopFrame() || fr == getBottomFrame())
+    {
+      /* 
+       * patch for JAL-3288 (deselecting top/bottom frame closes popup menu); 
+       * it may be possible to remove this method in future
+       * if the underlying Java behaviour changes
+       */
+      if (selected)
+      {
+        moveToFront();
+      }
+      return;
+    }
+    super.setSelected(selected);
+  }
 }
index 23b7fcf..33d8c33 100644 (file)
@@ -322,6 +322,11 @@ public class StructureChooser extends GStructureChooser
     boolean isUniProtRefsFound = false;
     StringBuilder queryBuilder = new StringBuilder();
     Set<String> seqRefs = new LinkedHashSet<>();
+    
+    /*
+     * note PDBs as DBRefEntry so they are not duplicated in query
+     */
+    Set<String> pdbids = new HashSet<>();
 
     if (seq.getAllPDBEntries() != null
             && queryBuilder.length() < MAX_QLENGTH)
@@ -330,9 +335,10 @@ public class StructureChooser extends GStructureChooser
       {
         if (isValidSeqName(entry.getId()))
         {
-          queryBuilder.append("pdb_id:").append(entry.getId().toLowerCase())
-                  .append(" OR ");
+          String id = entry.getId().toLowerCase();
+          queryBuilder.append("pdb_id:").append(id).append(" OR ");
           isPDBRefsFound = true;
+          pdbids.add(id);
         }
       }
     }
@@ -357,9 +363,13 @@ public class StructureChooser extends GStructureChooser
           else if (dbRef.getSource().equalsIgnoreCase(DBRefSource.PDB))
           {
 
-            queryBuilder.append("pdb_id:")
-                    .append(getDBRefId(dbRef).toLowerCase()).append(" OR ");
-            isPDBRefsFound = true;
+            String id = getDBRefId(dbRef).toLowerCase();
+            if (!pdbids.contains(id))
+            {
+              queryBuilder.append("pdb_id:").append(id).append(" OR ");
+              isPDBRefsFound = true;
+              pdbids.add(id);
+            }
           }
           else
           {
index c094b1c..0848a4d 100644 (file)
@@ -688,7 +688,8 @@ public class VamsasApplication implements SelectionSource, VamsasSource
 
   public void disableGui(boolean b)
   {
-    Desktop.instance.setVamsasUpdate(b);
+    // JAL-3311 TODO: remove this class!
+    // Desktop.instance.setVamsasUpdate(b);
   }
 
   Hashtable _backup_vobj2jv;
index c6137cc..25ade21 100644 (file)
@@ -26,12 +26,13 @@ import jalview.ws.WSClientI;
 
 import java.awt.BorderLayout;
 import java.awt.Color;
-import java.awt.Font;
+import java.awt.Dimension;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.GridLayout;
 import java.awt.Image;
 import java.awt.MediaTracker;
+import java.awt.RenderingHints;
 import java.awt.event.ActionEvent;
 import java.awt.image.BufferedImage;
 import java.util.Vector;
@@ -45,6 +46,8 @@ import javax.swing.JTabbedPane;
 import javax.swing.JTextArea;
 import javax.swing.event.HyperlinkEvent;
 import javax.swing.event.HyperlinkListener;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
 import javax.swing.text.html.HTMLEditorKit;
 import javax.swing.text.html.StyleSheet;
 
@@ -81,7 +84,7 @@ public class WebserviceInfo extends GWebserviceInfo
 
   Image image;
 
-  int angle = 0;
+  float angle = 0f;
 
   String title = "";
 
@@ -98,7 +101,7 @@ public class WebserviceInfo extends GWebserviceInfo
   {
     super.setVisible(aFlag);
     frame.setVisible(aFlag);
-  };
+  }
 
   JTabbedPane subjobs = null;
 
@@ -328,7 +331,7 @@ public class WebserviceInfo extends GWebserviceInfo
     setInfoText(info);
 
     java.net.URL url = getClass()
-            .getResource("/images/Jalview_Logo_small.png");
+            .getResource("/images/Jalview_Logo_small_with_border.png");
     image = java.awt.Toolkit.getDefaultToolkit().createImage(url);
 
     MediaTracker mt = new MediaTracker(this);
@@ -342,17 +345,19 @@ public class WebserviceInfo extends GWebserviceInfo
     }
 
     AnimatedPanel ap = new AnimatedPanel();
-    titlePanel.add(ap, BorderLayout.CENTER);
+    ap.setPreferredSize(new Dimension(60, 60));
+    titlePanel.add(ap, BorderLayout.WEST);
+    titlePanel.add(titleText, BorderLayout.CENTER);
+    setStatus(currentStatus);
 
     Thread thread = new Thread(ap);
     thread.start();
     final WebserviceInfo thisinfo = this;
     frame.addInternalFrameListener(
-            new javax.swing.event.InternalFrameAdapter()
+            new InternalFrameAdapter()
             {
               @Override
-              public void internalFrameClosed(
-                      javax.swing.event.InternalFrameEvent evt)
+              public void internalFrameClosed(InternalFrameEvent evt)
               {
                 // System.out.println("Shutting down webservice client");
                 WSClientI service = thisinfo.getthisService();
@@ -360,7 +365,7 @@ public class WebserviceInfo extends GWebserviceInfo
                 {
                   service.cancelJob();
                 }
-              };
+              }
             });
     frame.validate();
 
@@ -375,6 +380,36 @@ public class WebserviceInfo extends GWebserviceInfo
   public void setStatus(int status)
   {
     currentStatus = status;
+
+    String message = null;
+    switch (currentStatus)
+    {
+    case STATE_QUEUING:
+      message = MessageManager.getString("label.state_queueing");
+      break;
+
+    case STATE_RUNNING:
+      message = MessageManager.getString("label.state_running");
+      break;
+
+    case STATE_STOPPED_OK:
+      message = MessageManager.getString("label.state_completed");
+      break;
+
+    case STATE_CANCELLED_OK:
+      message = MessageManager.getString("label.state_job_cancelled");
+      break;
+
+    case STATE_STOPPED_ERROR:
+      message = MessageManager.getString("label.state_job_error");
+      break;
+
+    case STATE_STOPPED_SERVERERROR:
+      message = MessageManager.getString("label.server_error_try_later");
+      break;
+    }
+    titleText.setText(title + (message == null ? "" : " - " + message));
+    titleText.repaint();
   }
 
   /**
@@ -763,20 +798,33 @@ public class WebserviceInfo extends GWebserviceInfo
     {
       startTime = System.currentTimeMillis();
 
+      float invSpeed = 15f;
+      float factor = 1f;
       while (currentStatus < STATE_STOPPED_OK)
       {
+        if (currentStatus == STATE_QUEUING)
+        {
+          invSpeed = 25f;
+          factor = 1f;
+        }
+        else if (currentStatus == STATE_RUNNING)
+        {
+          invSpeed = 10f;
+          factor = (float) (0.5 + 1.5
+                  * (0.5 - (0.5 * Math.sin(3.14159 / 180 * (angle + 45)))));
+        }
         try
         {
           Thread.sleep(50);
 
-          int units = (int) ((System.currentTimeMillis() - startTime)
-                  / 10f);
-          angle += units;
+          float delta = (System.currentTimeMillis() - startTime) / invSpeed;
+          angle += delta * factor;
           angle %= 360;
           startTime = System.currentTimeMillis();
 
           if (currentStatus >= STATE_STOPPED_OK)
           {
+            park();
             angle = 0;
           }
 
@@ -789,87 +837,67 @@ public class WebserviceInfo extends GWebserviceInfo
       cancel.setEnabled(false);
     }
 
+    public void park()
+    {
+      startTime = System.currentTimeMillis();
+
+      while (angle < 360)
+      {
+        float invSpeed = 5f;
+        float factor = 1f;
+        try
+        {
+          Thread.sleep(25);
+
+          float delta = (System.currentTimeMillis() - startTime) / invSpeed;
+          angle += delta * factor;
+          startTime = System.currentTimeMillis();
+
+          if (angle >= 360)
+          {
+            angle = 360;
+          }
+
+          repaint();
+        } catch (Exception ex)
+        {
+        }
+      }
+
+    }
+
     void drawPanel()
     {
       if (offscreen == null || offscreen.getWidth(this) != getWidth()
               || offscreen.getHeight(this) != getHeight())
       {
         offscreen = new BufferedImage(getWidth(), getHeight(),
-                BufferedImage.TYPE_INT_ARGB);
+                BufferedImage.TYPE_INT_RGB);
       }
 
       Graphics2D g = (Graphics2D) offscreen.getGraphics();
 
+      g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+              RenderingHints.VALUE_ANTIALIAS_ON);
+      g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+              RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+      g.setRenderingHint(RenderingHints.KEY_RENDERING,
+              RenderingHints.VALUE_RENDER_QUALITY);
+
       g.setColor(Color.white);
       g.fillRect(0, 0, getWidth(), getHeight());
 
-      g.setFont(new Font("Arial", Font.BOLD, 12));
-      g.setColor(Color.black);
-
-      switch (currentStatus)
-      {
-      case STATE_QUEUING:
-        g.drawString(
-                title.concat(" - ").concat(
-                        MessageManager.getString("label.state_queueing")),
-                60, 30);
-
-        break;
-
-      case STATE_RUNNING:
-        g.drawString(
-                title.concat(" - ").concat(
-                        MessageManager.getString("label.state_running")),
-                60, 30);
-
-        break;
-
-      case STATE_STOPPED_OK:
-        g.drawString(
-                title.concat(" - ").concat(
-                        MessageManager.getString("label.state_completed")),
-                60, 30);
-
-        break;
-
-      case STATE_CANCELLED_OK:
-        g.drawString(
-                title.concat(" - ")
-                        .concat(MessageManager
-                                .getString("label.state_job_cancelled")),
-                60, 30);
-
-        break;
-
-      case STATE_STOPPED_ERROR:
-        g.drawString(
-                title.concat(" - ").concat(
-                        MessageManager.getString("label.state_job_error")),
-                60, 30);
-
-        break;
-
-      case STATE_STOPPED_SERVERERROR:
-        g.drawString(
-                title.concat(" - ")
-                        .concat(MessageManager
-                                .getString("label.server_error_try_later")),
-                60, 30);
-
-        break;
-      }
-
       if (image != null)
       {
         int x = image.getWidth(this) / 2, y = image.getHeight(this) / 2;
-        g.rotate(Math.toRadians(angle), 10 + x, 10 + y);
-        g.drawImage(image, 10, 10, this);
-        g.rotate(-Math.toRadians(angle), 10 + x, 10 + y);
+        g.rotate(3.14159 / 180 * (angle), x, y);
+        g.drawImage(image, 0, 0, this);
+        g.rotate(-3.14159 / 180 * (angle), x, y);
       }
     }
 
     @Override
-    public void paintComponent(Graphics g1)
+       public void paintComponent(Graphics g1)
     {
       drawPanel();
 
@@ -885,7 +913,7 @@ public class WebserviceInfo extends GWebserviceInfo
   }
 
   @Override
-  public void hyperlinkUpdate(HyperlinkEvent e)
+public void hyperlinkUpdate(HyperlinkEvent e)
   {
     Desktop.hyperlinkUpdate(e);
   }
index 850ef21..5186a26 100644 (file)
@@ -123,7 +123,7 @@ public class WsPreferences extends GWsPreferences
     for (String url : wsUrls)
     {
       int status = Jws2Discoverer.getDiscoverer().getServerStatusFor(url);
-      tdat[r][1] = new Integer(status);
+      tdat[r][1] = Integer.valueOf(status);
       tdat[r++][0] = url;
     }
 
index 02667c5..f0f1ca3 100755 (executable)
@@ -274,7 +274,7 @@ public class AnnotationFile
           if (row.graphGroup > -1)
           {
             graphGroupSeen.set(row.graphGroup);
-            Integer key = new Integer(row.graphGroup);
+            Integer key = Integer.valueOf(row.graphGroup);
             if (graphGroup.containsKey(key))
             {
               graphGroup.put(key, graphGroup.get(key) + "\t" + row.label);
@@ -762,7 +762,7 @@ public class AnnotationFile
                       autoAnnotsKey(aa[aai], aa[aai].sequenceRef,
                               (aa[aai].groupRef == null ? null
                                       : aa[aai].groupRef.getName())),
-                      new Integer(1));
+                      Integer.valueOf(1));
             }
           }
         }
@@ -1271,7 +1271,7 @@ public class AnnotationFile
         {
           displayChar = token;
           // foo
-          value = new Float(token).floatValue();
+          value = Float.valueOf(token).floatValue();
           parsedValue = true;
           continue;
         } catch (NumberFormatException ex)
index 4f93ece..1504404 100644 (file)
@@ -1,7 +1,5 @@
 package jalview.io;
 
-import jalview.bin.Cache;
-
 import java.io.File;
 
 public class BackupFilenameParts
@@ -113,7 +111,9 @@ public class BackupFilenameParts
           String filename, String base, boolean extensionMatch)
   {
     BackupFilenameParts bfp = new BackupFilenameParts();
-    String template = Cache.getDefault(BackupFiles.SUFFIX, null);
+    BackupFilesPresetEntry bfpe = BackupFilesPresetEntry
+            .getSavedBackupEntry();
+    String template = bfpe.suffix;
     if (template == null)
     {
       return bfp;
@@ -121,8 +121,7 @@ public class BackupFilenameParts
     int digits;
     try
     {
-      digits = Integer
-              .parseInt(Cache.getDefault(BackupFiles.SUFFIX_DIGITS, null));
+      digits = bfpe.digits;
     } catch (Exception e)
     {
       return bfp;
index dae1c02..06c2fc9 100644 (file)
@@ -18,12 +18,8 @@ import java.util.TreeMap;
  * BackupFiles used for manipulating (naming rolling/deleting) backup/version files when an alignment or project file is saved.
  * User configurable options are:
  * BACKUPFILES_ENABLED - boolean flag as to whether to use this mechanism or act as before, including overwriting files as saved.
- * BACKUPFILES_SUFFIX - a template to insert after the file extension.  Use '%n' to be replaced by a 0-led SUFFIX_DIGITS long integer.
- * BACKUPFILES_NO_MAX - flag to turn off setting a maximum number of backup files to keep.
- * BACKUPFILES_ROLL_MAX - the maximum number of backupfiles to keep for any one alignment or project file.
- * BACKUPFILES_SUFFIX_DIGITS - the number of digits to insert replace %n with (e.g. BACKUPFILES_SUFFIX_DIGITS = 3 would make "001", "002", etc)
- * BACKUPFILES_REVERSE_ORDER - if true then "logfile" style numbering and file rolling will occur. If false then ever-increasing version numbering will occur, but old files will still be deleted if there are more than ROLL_MAX backup files. 
- * BACKUPFILES_CONFIRM_DELETE_OLD - if true then prompt/confirm with the user when deleting older backup/version files.
+ * The rest of the options are now saved as BACKUPFILES_PRESET, BACKUPFILES_SAVED and BACKUPFILES_CUSTOM
+ * (see BackupFilesPresetEntry)
  */
 
 public class BackupFiles
@@ -34,21 +30,8 @@ public class BackupFiles
 
   public static final String ENABLED = NS + "_ENABLED";
 
-  public static final String SUFFIX = NS + "_SUFFIX";
-
-  public static final String NO_MAX = NS + "_NO_MAX";
-
-  public static final String ROLL_MAX = NS + "_ROLL_MAX";
-
-  public static final String SUFFIX_DIGITS = NS + "_SUFFIX_DIGITS";
-
   public static final String NUM_PLACEHOLDER = "%n";
 
-  public static final String REVERSE_ORDER = NS + "_REVERSE_ORDER";
-
-  public static final String CONFIRM_DELETE_OLD = NS
-          + "_CONFIRM_DELETE_OLD";
-
   private static final String DEFAULT_TEMP_FILE = "jalview_temp_file_" + NS;
 
   private static final String TEMP_FILE_EXT = ".tmp";
@@ -102,20 +85,14 @@ public class BackupFiles
   // REVERSE_ORDER
   public BackupFiles(File file)
   {
-    this(file, ".bak" + NUM_PLACEHOLDER, false, 3, 3, false);
-  }
-
-  public BackupFiles(File file, String defaultSuffix, boolean defaultNoMax,
-          int defaultMax, int defaultDigits, boolean defaultReverseOrder)
-  {
     classInit();
     this.file = file;
-    this.suffix = Cache.getDefault(SUFFIX, defaultSuffix);
-    this.noMax = Cache.getDefault(NO_MAX, defaultNoMax);
-    this.max = Cache.getDefault(ROLL_MAX, defaultMax);
-    this.digits = Cache.getDefault(SUFFIX_DIGITS, defaultDigits);
-    this.reverseOrder = Cache.getDefault(REVERSE_ORDER,
-            defaultReverseOrder);
+    BackupFilesPresetEntry bfpe = BackupFilesPresetEntry.getSavedBackupEntry();
+    this.suffix = bfpe.suffix;
+    this.noMax = bfpe.keepAll;
+    this.max = bfpe.rollMax;
+    this.digits = bfpe.digits;
+    this.reverseOrder = bfpe.reverse;
 
     // create a temp file to save new data in
     File temp = null;
@@ -146,7 +123,9 @@ public class BackupFiles
   public static void classInit()
   {
     setEnabled(Cache.getDefault(ENABLED, !Platform.isJS()));
-    setConfirmDelete(Cache.getDefault(CONFIRM_DELETE_OLD, true));
+    BackupFilesPresetEntry bfpe = BackupFilesPresetEntry
+            .getSavedBackupEntry();
+    setConfirmDelete(bfpe.confirmDelete);
   }
 
   public static void setEnabled(boolean flag)
diff --git a/src/jalview/io/BackupFilesPresetEntry.java b/src/jalview/io/BackupFilesPresetEntry.java
new file mode 100644 (file)
index 0000000..4face29
--- /dev/null
@@ -0,0 +1,173 @@
+package jalview.io;
+
+import jalview.bin.Cache;
+import jalview.util.MessageManager;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+public class BackupFilesPresetEntry
+{
+
+  public String suffix;
+
+  public static final int DIGITSMIN = 1;
+
+  public static final int DIGITSMAX = 6;
+
+  public int digits;
+
+  public boolean reverse;
+
+  public boolean keepAll;
+
+  public static final int ROLLMAXMIN = 1;
+
+  public static final int ROLLMAXMAX = 999;
+
+  public int rollMax;
+
+  public boolean confirmDelete;
+
+  public static final String SAVEDCONFIG = BackupFiles.NS + "_SAVED";
+
+  public static final String CUSTOMCONFIG = BackupFiles.NS + "_CUSTOM";
+
+  private static final String stringDelim = "\t";
+
+  public static final int BACKUPFILESSCHEMECUSTOM = 0;
+
+  public static final int BACKUPFILESSCHEMEDEFAULT = 1;
+
+  public BackupFilesPresetEntry(String suffix, int digits, boolean reverse,
+          boolean keepAll, int rollMax, boolean confirmDelete)
+  {
+    this.suffix = suffix == null ? "" : suffix;
+    this.digits = digits < DIGITSMIN ? DIGITSMIN
+            : (digits > DIGITSMAX ? DIGITSMAX : digits);
+    this.reverse = reverse;
+    this.keepAll = keepAll;
+    this.rollMax = rollMax < ROLLMAXMIN ? ROLLMAXMIN
+            : (rollMax > ROLLMAXMAX ? ROLLMAXMAX : rollMax);
+    this.confirmDelete = confirmDelete;
+  }
+
+  public boolean equals(BackupFilesPresetEntry compare)
+  {
+    return suffix.equals(compare.suffix) && digits == compare.digits
+            && reverse == compare.reverse && keepAll == compare.keepAll
+            && rollMax == compare.rollMax
+            && confirmDelete == compare.confirmDelete;
+  }
+
+  @Override
+  public String toString()
+  {
+    StringBuilder sb = new StringBuilder();
+    sb.append(suffix);
+    sb.append(stringDelim);
+    sb.append(digits);
+    sb.append(stringDelim);
+    sb.append(reverse);
+    sb.append(stringDelim);
+    sb.append(keepAll);
+    sb.append(stringDelim);
+    sb.append(rollMax);
+    sb.append(stringDelim);
+    sb.append(confirmDelete);
+    return sb.toString();
+  }
+
+  public static BackupFilesPresetEntry createBackupFilesPresetEntry(
+          String line)
+  {
+    if (line == null)
+    {
+      return null;
+    }
+    StringTokenizer st = new StringTokenizer(line, stringDelim);
+    String suffix = null;
+    int digits = 0;
+    boolean reverse = false;
+    boolean keepAll = false;
+    int rollMax = 0;
+    boolean confirmDelete = false;
+
+    try
+    {
+      suffix = st.nextToken();
+      digits = Integer.valueOf(st.nextToken());
+      reverse = Boolean.valueOf(st.nextToken());
+      keepAll = Boolean.valueOf(st.nextToken());
+      rollMax = Integer.valueOf(st.nextToken());
+      confirmDelete = Boolean.valueOf(st.nextToken());
+    } catch (Exception e)
+    {
+      Cache.log.error("Error parsing backupfiles scheme '" + line + "'");
+    }
+
+    return new BackupFilesPresetEntry(suffix, digits, reverse, keepAll,
+            rollMax, confirmDelete);
+  }
+
+  public static BackupFilesPresetEntry getSavedBackupEntry()
+  {
+    String savedPresetString = Cache
+            .getDefault(BackupFilesPresetEntry.SAVEDCONFIG, null);
+    BackupFilesPresetEntry savedPreset = BackupFilesPresetEntry
+            .createBackupFilesPresetEntry(savedPresetString);
+    if (savedPreset == null)
+    {
+      savedPreset = backupfilesPresetEntriesValues
+              .get(BACKUPFILESSCHEMEDEFAULT);
+    }
+    return savedPreset;
+  }
+
+  public static final IntKeyStringValueEntry[] backupfilesPresetEntries = {
+      new IntKeyStringValueEntry(BACKUPFILESSCHEMEDEFAULT,
+              MessageManager.getString("label.default")),
+      new IntKeyStringValueEntry(2,
+              MessageManager.getString("label.single_file")),
+      new IntKeyStringValueEntry(3,
+              MessageManager.getString("label.keep_all_versions")),
+      new IntKeyStringValueEntry(4,
+              MessageManager.getString("label.rolled_backups")),
+      // ...
+      // IMPORTANT, keep "Custom" entry with key 0 (even though it appears last)
+      new IntKeyStringValueEntry(BACKUPFILESSCHEMECUSTOM,
+              MessageManager.getString("label.custom")) };
+
+  public static final String[] backupfilesPresetEntryDescriptions = {
+      MessageManager.getString("label.default_description"),
+      MessageManager.getString("label.single_file_description"),
+      MessageManager.getString("label.keep_all_versions_description"),
+      MessageManager.getString("label.rolled_backups_description"),
+      MessageManager.getString("label.custom_description") };
+
+  public static final Map<Integer, BackupFilesPresetEntry> backupfilesPresetEntriesValues = new HashMap<Integer, BackupFilesPresetEntry>()
+  {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 125L;
+
+    {
+      put(1, new BackupFilesPresetEntry(
+              ".bak" + BackupFiles.NUM_PLACEHOLDER, 3, false, false, 3,
+              false));
+      put(2, new BackupFilesPresetEntry("~", 1, false, false, 1, false));
+      put(3, new BackupFilesPresetEntry(".v" + BackupFiles.NUM_PLACEHOLDER,
+              3, false, true, 10, true));
+      put(4, new BackupFilesPresetEntry(
+              "_bak." + BackupFiles.NUM_PLACEHOLDER, 1, true, false, 9,
+              false));
+
+      // This gets replaced by GPreferences
+      put(BACKUPFILESSCHEMECUSTOM,
+              new BackupFilesPresetEntry("", 0, false, false, 0, false));
+    }
+  };
+
+}
index d51da33..5f68099 100755 (executable)
@@ -441,7 +441,7 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
       float score = Float.NaN;
       try
       {
-        score = new Float(gffColumns[6]).floatValue();
+        score = Float.valueOf(gffColumns[6]).floatValue();
       } catch (NumberFormatException ex)
       {
         sf = new SequenceFeature(ft, desc, startPos, endPos, featureGroup);
index 95f85e6..1d5be1b 100755 (executable)
@@ -214,6 +214,7 @@ public class FileLoader implements Runnable
   protected AlignFrame _LoadFileWaitTillLoaded()
   {
     this.run();
+
     return alignFrame;
   }
 
diff --git a/src/jalview/io/IntKeyStringValueEntry.java b/src/jalview/io/IntKeyStringValueEntry.java
new file mode 100644 (file)
index 0000000..084dbc5
--- /dev/null
@@ -0,0 +1,21 @@
+package jalview.io;
+
+public class IntKeyStringValueEntry
+{
+  public final int k;
+
+  public final String v;
+
+  public IntKeyStringValueEntry(int k, String v)
+  {
+    this.k = k;
+    this.v = v;
+  }
+
+  @Override
+  public String toString()
+  {
+    return v;
+  }
+
+}
index e375df7..6af92b7 100755 (executable)
@@ -209,7 +209,7 @@ public class JPredFile extends AlignFile
           {
             ascore = symbols.nextToken();
 
-            Float score = new Float(ascore);
+            Float score = Float.valueOf(ascore);
             scores.addElement(score);
           }
 
@@ -285,7 +285,7 @@ public class JPredFile extends AlignFile
 
           seq_entries.addElement(newseq.toString());
           ids.addElement(id);
-          Symscores.put(id, new Integer(ids.size() - 1));
+          Symscores.put(id, Integer.valueOf(ids.size() - 1));
         }
       }
     }
index c9f1fcf..6828202 100755 (executable)
@@ -148,7 +148,7 @@ public class JnetAnnotationMaker
             {
               for (int j = 0; j < width; j++)
               {
-                float value = new Float(preds[i].getCharAt(j) + "")
+                float value = Float.valueOf(preds[i].getCharAt(j) + "")
                         .floatValue();
                 annotations[gapmap[j]] = new Annotation(
                         preds[i].getCharAt(j) + "", "",
@@ -159,7 +159,7 @@ public class JnetAnnotationMaker
             {
               for (int j = 0; j < width; j++)
               {
-                float value = new Float(preds[i].getCharAt(j) + "")
+                float value = Float.valueOf(preds[i].getCharAt(j) + "")
                         .floatValue();
                 annotations[gapmap[delMap[j]]] = new Annotation(
                         preds[i].getCharAt(j) + "", "",
index f4de52d..a56f2af 100755 (executable)
@@ -90,7 +90,7 @@ public class ModellerDescription
 
     resCode(int v)
     {
-      val = new Integer(v);
+      val = Integer.valueOf(v);
       field = val.toString();
     }
   };
index 1e9c5ed..f3eaa45 100755 (executable)
@@ -475,7 +475,7 @@ public class NewickFile extends FileParse
           {
             try
             {
-              bootstrap = (new Integer(nbootstrap.stringMatched(1)))
+              bootstrap = (Integer.valueOf(nbootstrap.stringMatched(1)))
                       .intValue();
               HasBootstrap = true;
             } catch (Exception e)
@@ -492,7 +492,7 @@ public class NewickFile extends FileParse
         {
           try
           {
-            distance = (new Float(ndist.stringMatched(1))).floatValue();
+            distance = (Float.valueOf(ndist.stringMatched(1))).floatValue();
             HasDistances = true;
             nodehasdistance = true;
           } catch (Exception e)
@@ -659,7 +659,7 @@ public class NewickFile extends FileParse
             if (code.toLowerCase().equals("b"))
             {
               int v = -1;
-              Float iv = new Float(value);
+              Float iv = Float.valueOf(value);
               v = iv.intValue(); // jalview only does integer bootstraps
               // currently
               c.setBootstrap(v);
index 27e4da2..2381ecf 100644 (file)
@@ -23,6 +23,7 @@ package jalview.io;
 import jalview.api.FeatureColourI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
+import jalview.datamodel.GeneLociI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.util.MessageManager;
@@ -66,11 +67,11 @@ public class SequenceAnnotationReport
     @Override
     public int compare(DBRefEntry ref1, DBRefEntry ref2)
     {
-      if (ref1.isChromosome())
+      if (ref1 instanceof GeneLociI)
       {
         return -1;
       }
-      if (ref2.isChromosome())
+      if (ref2 instanceof GeneLociI)
       {
         return 1;
       }
index adf4447..387dbfa 100644 (file)
@@ -2248,7 +2248,7 @@ public class VamsasAppDatastore
           Float val = null;
           try
           {
-            val = new Float(props[p].getContent());
+            val = Float.valueOf(props[p].getContent());
           } catch (Exception e)
           {
             Cache.log.warn("Failed to parse threshold property");
@@ -2537,7 +2537,7 @@ public class VamsasAppDatastore
           int se_end = se[1 - se[2]] + (se[2] == 0 ? 1 : -1);
           for (int p = se[se[2]]; p != se_end; p += se[2] == 0 ? 1 : -1)
           {
-            posList.add(new Integer(p));
+            posList.add(Integer.valueOf(p));
           }
         }
       }
@@ -2548,7 +2548,7 @@ public class VamsasAppDatastore
         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
         {
           pos = dseta.getPos(p).getI();
-          posList.add(new Integer(pos));
+          posList.add(Integer.valueOf(pos));
         }
       }
     }
index 72e906c..7d354e0 100644 (file)
@@ -70,6 +70,7 @@ public class SequenceOntologyLite implements SequenceOntologyI
     { "snRNA", "transcript" },
     { "miRNA", "transcript" },
     { "lincRNA", "transcript" },
+    { "lnc_RNA", "transcript" },
     { "rRNA", "transcript" },
     { "mRNA", "transcript" },
     // there are many more sub-types of ncRNA...
index ff7a764..b53de08 100644 (file)
@@ -24,7 +24,7 @@ import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.Map;
 
-public class DatastoreRegistry
+public class DatastoreRegistry implements AutoCloseable
 {
   protected static org.apache.log4j.Logger log = org.apache.log4j.Logger
           .getLogger(DatastoreRegistry.class);
@@ -153,7 +153,7 @@ public class DatastoreRegistry
   }
 
   @Override
-  protected void finalize() throws Throwable
+  public void close()
   {
     if (dsObjReg != null)
     {
@@ -172,6 +172,6 @@ public class DatastoreRegistry
     {
       dsItemReg.clear();
     }
-    super.finalize();
+    // super.finalize();
   }
 }
index ee4fa83..b4580f0 100644 (file)
@@ -148,7 +148,7 @@ public abstract class Rangetype extends DatastoreItem
           int se_end = se[1 - se[2]] + (se[2] == 0 ? 1 : -1);
           for (int p = se[se[2]]; p != se_end; p += se[2] == 0 ? 1 : -1)
           {
-            posList.add(new Integer(p));
+            posList.add(Integer.valueOf(p));
           }
         }
       }
@@ -159,7 +159,7 @@ public abstract class Rangetype extends DatastoreItem
         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
         {
           pos = dseta.getPos(p).getI();
-          posList.add(new Integer(pos));
+          posList.add(Integer.valueOf(pos));
         }
       }
     }
@@ -193,8 +193,8 @@ public abstract class Rangetype extends DatastoreItem
         for (int s = 0, sSize = range.getSegCount(); s < sSize; s++)
         {
           se = getSegRange(range.getSeg(s), false);
-          posList.addElement(new Integer(se[0]));
-          posList.addElement(new Integer(se[1]));
+          posList.addElement(Integer.valueOf(se[0]));
+          posList.addElement(Integer.valueOf(se[1]));
         }
       }
       else if (range.getPosCount() > 0)
@@ -204,8 +204,8 @@ public abstract class Rangetype extends DatastoreItem
         for (int p = 0, pSize = range.getPosCount(); p < pSize; p++)
         {
           pos = range.getPos(p).getI();
-          posList.add(new Integer(pos));
-          posList.add(new Integer(pos));
+          posList.add(Integer.valueOf(pos));
+          posList.add(Integer.valueOf(pos));
         }
       }
     }
index 74f73d4..f8d86d5 100644 (file)
@@ -351,7 +351,7 @@ public class Sequencefeature extends Rangetype
         {
           try
           {
-            val = new Boolean(p.getContent());
+            val = Boolean.valueOf(p.getContent());
           } catch (Exception e)
           {
           }
@@ -360,7 +360,7 @@ public class Sequencefeature extends Rangetype
         {
           try
           {
-            val = new Float(p.getContent());
+            val = Float.valueOf(p.getContent());
 
           } catch (Exception e)
           {
@@ -370,7 +370,7 @@ public class Sequencefeature extends Rangetype
         {
           try
           {
-            val = new Integer(p.getContent());
+            val = Integer.valueOf(p.getContent());
           } catch (Exception e)
           {
           }
index c0f65c4..00e4fbc 100644 (file)
@@ -443,7 +443,7 @@ public class Tree extends DatastoreItem
     Integer nindx = (Integer) nodespecs.get(nname);
     if (nindx == null)
     {
-      nindx = new Integer(1);
+      nindx = Integer.valueOf(1);
     }
     nname = nindx.toString() + " " + nname;
     return nname;
@@ -465,7 +465,7 @@ public class Tree extends DatastoreItem
     String oval = nodespec.substring(0, nodespec.indexOf(' '));
     try
     {
-      occurence = new Integer(oval).intValue();
+      occurence = Integer.valueOf(oval).intValue();
     } catch (Exception e)
     {
       System.err.println("Invalid nodespec '" + nodespec + "'");
index bf988da..990952d 100644 (file)
@@ -51,6 +51,8 @@ import htsjdk.variant.vcf.VCFInfoHeaderLine;
  */
 public class VCFLoader
 {
+  private static final String DEFAULT_SPECIES = "homo_sapiens";
+
   /**
    * A class to model the mapping from sequence to VCF coordinates. Cases include
    * <ul>
@@ -82,7 +84,7 @@ public class VCFLoader
 
   /*
    * Lookup keys, and default values, for Preference entries that describe
-   * patterns for VCF and VEP fields to capture 
+   * patterns for VCF and VEP fields to capture
    */
   private static final String VEP_FIELDS_PREF = "VEP_FIELDS";
 
@@ -93,6 +95,18 @@ public class VCFLoader
   private static final String DEFAULT_VEP_FIELDS = ".*";// "Allele,Consequence,IMPACT,SWISSPROT,SIFT,PolyPhen,CLIN_SIG";
 
   /*
+   * Lookup keys, and default values, for Preference entries that give
+   * mappings from tokens in the 'reference' header to species or assembly
+   */
+  private static final String VCF_ASSEMBLY = "VCF_ASSEMBLY";
+
+  private static final String DEFAULT_VCF_ASSEMBLY = "assembly19=GRCh37,hs37=GRCh37,grch37=GRCh37,grch38=GRCh38";
+
+  private static final String VCF_SPECIES = "VCF_SPECIES"; // default is human
+
+  private static final String DEFAULT_REFERENCE = "grch37"; // fallback default is human GRCh37
+
+  /*
    * keys to fields of VEP CSQ consequence data
    * see https://www.ensembl.org/info/docs/tools/vep/vep_formats.html
    */
@@ -114,12 +128,6 @@ public class VCFLoader
   private static final String PIPE_REGEX = "\\|";
 
   /*
-   * key for Allele Frequency output by VEP
-   * see http://www.ensembl.org/info/docs/tools/vep/vep_formats.html
-   */
-  private static final String ALLELE_FREQUENCY_KEY = "AF";
-
-  /*
    * delimiter that separates multiple consequence data blocks
    */
   private static final String COMMA = ",";
@@ -155,6 +163,16 @@ public class VCFLoader
   private VCFHeader header;
 
   /*
+   * species (as a valid Ensembl term) the VCF is for 
+   */
+  private String vcfSpecies;
+
+  /*
+   * genome assembly version (as a valid Ensembl identifier) the VCF is for 
+   */
+  private String vcfAssembly;
+
+  /*
    * a Dictionary of contigs (if present) referenced in the VCF file
    */
   private SAMSequenceDictionary dictionary;
@@ -246,12 +264,18 @@ public class VCFLoader
    */
   public SequenceI loadVCFContig(String contig)
   {
-    String ref = header.getOtherHeaderLine(VCFHeader.REFERENCE_KEY)
-            .getValue();
+    VCFHeaderLine headerLine = header.getOtherHeaderLine(VCFHeader.REFERENCE_KEY);
+    if (headerLine == null)
+    {
+      Cache.log.error("VCF reference header not found");
+      return null;
+    }
+    String ref = headerLine.getValue();
     if (ref.startsWith("file://"))
     {
       ref = ref.substring(7);
     }
+    setSpeciesAndAssembly(ref);
 
     SequenceI seq = null;
     File dbFile = new File(ref);
@@ -260,12 +284,12 @@ public class VCFLoader
     {
       HtsContigDb db = new HtsContigDb("", dbFile);
       seq = db.getSequenceProxy(contig);
-      loadSequenceVCF(seq, ref);
+      loadSequenceVCF(seq);
       db.close();
     }
     else
     {
-      System.err.println("VCF reference not found: " + ref);
+      Cache.log.error("VCF reference not found: " + ref);
     }
 
     return seq;
@@ -284,7 +308,9 @@ public class VCFLoader
     {
       VCFHeaderLine ref = header
               .getOtherHeaderLine(VCFHeader.REFERENCE_KEY);
-      String vcfAssembly = ref.getValue();
+      String reference = ref == null ? null : ref.getValue();
+
+      setSpeciesAndAssembly(reference);
 
       int varCount = 0;
       int seqCount = 0;
@@ -294,7 +320,7 @@ public class VCFLoader
        */
       for (SequenceI seq : seqs)
       {
-        int added = loadSequenceVCF(seq, vcfAssembly);
+        int added = loadSequenceVCF(seq);
         if (added > 0)
         {
           seqCount++;
@@ -338,6 +364,71 @@ public class VCFLoader
   }
 
   /**
+   * Attempts to determine and save the species and genome assembly version to
+   * which the VCF data applies. This may be done by parsing the {@code reference}
+   * header line, configured in a property file, or (potentially) confirmed
+   * interactively by the user.
+   * <p>
+   * The saved values should be identifiers valid for Ensembl's REST service
+   * {@code map} endpoint, so they can be used (if necessary) to retrieve the
+   * mapping between VCF coordinates and sequence coordinates.
+   * 
+   * @param reference
+   * @see https://rest.ensembl.org/documentation/info/assembly_map
+   * @see https://rest.ensembl.org/info/assembly/human?content-type=text/xml
+   * @see https://rest.ensembl.org/info/species?content-type=text/xml
+   */
+  protected void setSpeciesAndAssembly(String reference)
+  {
+    if (reference == null)
+    {
+      Cache.log.error("No VCF ##reference found, defaulting to "
+              + DEFAULT_REFERENCE + ":" + DEFAULT_SPECIES);
+      reference = DEFAULT_REFERENCE; // default to GRCh37 if not specified
+    }
+    reference = reference.toLowerCase();
+
+    /*
+     * for a non-human species, or other assembly identifier,
+     * specify as a Jalview property file entry e.g.
+     * VCF_ASSEMBLY = hs37=GRCh37,assembly19=GRCh37
+     * VCF_SPECIES = c_elegans=celegans
+     * to map a token in the reference header to a value
+     */
+    String prop = Cache.getDefault(VCF_ASSEMBLY, DEFAULT_VCF_ASSEMBLY);
+    for (String token : prop.split(","))
+    {
+      String[] tokens = token.split("=");
+      if (tokens.length == 2)
+      {
+        if (reference.contains(tokens[0].trim().toLowerCase()))
+        {
+          vcfAssembly = tokens[1].trim();
+          break;
+        }
+      }
+    }
+
+    vcfSpecies = DEFAULT_SPECIES;
+    prop = Cache.getProperty(VCF_SPECIES);
+    if (prop != null)
+    {
+      for (String token : prop.split(","))
+      {
+        String[] tokens = token.split("=");
+        if (tokens.length == 2)
+        {
+          if (reference.contains(tokens[0].trim().toLowerCase()))
+          {
+            vcfSpecies = tokens[1].trim();
+            break;
+          }
+        }
+      }
+    }
+  }
+
+  /**
    * Opens the VCF file and parses header data
    * 
    * @param filePath
@@ -588,12 +679,11 @@ public class VCFLoader
    * and returns the number of variant features added
    * 
    * @param seq
-   * @param vcfAssembly
    * @return
    */
-  protected int loadSequenceVCF(SequenceI seq, String vcfAssembly)
+  protected int loadSequenceVCF(SequenceI seq)
   {
-    VCFMap vcfMap = getVcfMap(seq, vcfAssembly);
+    VCFMap vcfMap = getVcfMap(seq);
     if (vcfMap == null)
     {
       return 0;
@@ -614,10 +704,9 @@ public class VCFLoader
    * Answers a map from sequence coordinates to VCF chromosome ranges
    * 
    * @param seq
-   * @param vcfAssembly
    * @return
    */
-  private VCFMap getVcfMap(SequenceI seq, String vcfAssembly)
+  private VCFMap getVcfMap(SequenceI seq)
   {
     /*
      * simplest case: sequence has id and length matching a VCF contig
@@ -648,34 +737,28 @@ public class VCFLoader
     String species = seqCoords.getSpeciesId();
     String chromosome = seqCoords.getChromosomeId();
     String seqRef = seqCoords.getAssemblyId();
-    MapList map = seqCoords.getMap();
+    MapList map = seqCoords.getMapping();
 
-    if (!vcfSpeciesMatchesSequence(vcfAssembly, species))
+    // note this requires the configured species to match that
+    // returned with the Ensembl sequence; todo: support aliases?
+    if (!vcfSpecies.equalsIgnoreCase(species))
     {
+      Cache.log.warn("No VCF loaded to " + seq.getName()
+              + " as species not matched");
       return null;
     }
 
-    if (vcfAssemblyMatchesSequence(vcfAssembly, seqRef))
+    if (seqRef.equalsIgnoreCase(vcfAssembly))
     {
       return new VCFMap(chromosome, map);
     }
 
-    if (!"GRCh38".equalsIgnoreCase(seqRef) // Ensembl
-            || !vcfAssembly.contains("Homo_sapiens_assembly19")) // gnomAD
-    {
-      return null;
-    }
-
     /*
-     * map chromosomal coordinates from sequence to VCF if the VCF
-     * data has a different reference assembly to the sequence
+     * VCF data has a different reference assembly to the sequence:
+     * query Ensembl to map chromosomal coordinates from sequence to VCF
      */
-    // TODO generalise for cases other than GRCh38 -> GRCh37 !
-    // - or get the user to choose in a dialog
-
     List<int[]> toVcfRanges = new ArrayList<>();
     List<int[]> fromSequenceRanges = new ArrayList<>();
-    String toRef = "GRCh37";
 
     for (int[] range : map.getToRanges())
     {
@@ -687,12 +770,13 @@ public class VCFLoader
       }
 
       int[] newRange = mapReferenceRange(range, chromosome, "human", seqRef,
-              toRef);
+              vcfAssembly);
       if (newRange == null)
       {
         Cache.log.error(
                 String.format("Failed to map %s:%s:%s:%d:%d to %s", species,
-                        chromosome, seqRef, range[0], range[1], toRef));
+                        chromosome, seqRef, range[0], range[1],
+                        vcfAssembly));
         continue;
       }
       else
@@ -733,62 +817,6 @@ public class VCFLoader
   }
 
   /**
-   * Answers true if we determine that the VCF data uses the same reference
-   * assembly as the sequence, else false
-   * 
-   * @param vcfAssembly
-   * @param seqRef
-   * @return
-   */
-  private boolean vcfAssemblyMatchesSequence(String vcfAssembly,
-          String seqRef)
-  {
-    // TODO improve on this stub, which handles gnomAD and
-    // hopes for the best for other cases
-
-    if ("GRCh38".equalsIgnoreCase(seqRef) // Ensembl
-            && vcfAssembly.contains("Homo_sapiens_assembly19")) // gnomAD
-    {
-      return false;
-    }
-    return true;
-  }
-
-  /**
-   * Answers true if the species inferred from the VCF reference identifier
-   * matches that for the sequence
-   * 
-   * @param vcfAssembly
-   * @param speciesId
-   * @return
-   */
-  boolean vcfSpeciesMatchesSequence(String vcfAssembly, String speciesId)
-  {
-    // PROBLEM 1
-    // there are many aliases for species - how to equate one with another?
-    // PROBLEM 2
-    // VCF ##reference header is an unstructured URI - how to extract species?
-    // perhaps check if ref includes any (Ensembl) alias of speciesId??
-    // TODO ask the user to confirm this??
-
-    if (vcfAssembly.contains("Homo_sapiens") // gnomAD exome data example
-            && "HOMO_SAPIENS".equals(speciesId)) // Ensembl species id
-    {
-      return true;
-    }
-
-    if (vcfAssembly.contains("c_elegans") // VEP VCF response example
-            && "CAENORHABDITIS_ELEGANS".equals(speciesId)) // Ensembl
-    {
-      return true;
-    }
-
-    // this is not a sustainable solution...
-
-    return false;
-  }
-
-  /**
    * Queries the VCF reader for any variants that overlap the mapped chromosome
    * ranges of the sequence, and adds as variant features. Returns the number of
    * overlapping variants found.
index 8bbfb8d..6685c67 100755 (executable)
@@ -35,7 +35,6 @@ import jalview.util.Platform;
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.GridLayout;
-import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.FocusAdapter;
@@ -277,14 +276,14 @@ public class GAlignFrame extends JInternalFrame
 
     // FIXME getDefaultToolkit throws an exception in Headless mode
     KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
-                    | KeyEvent.SHIFT_MASK,
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
+                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
             false);
     addMenuActionAndAccelerator(keyStroke, saveAs, al);
 
     closeMenuItem.setText(MessageManager.getString("action.close"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_W,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -307,7 +306,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem selectAllSequenceMenuItem = new JMenuItem(
             MessageManager.getString("action.select_all"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_A,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -334,7 +333,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem invertSequenceMenuItem = new JMenuItem(
             MessageManager.getString("action.invert_sequence_selection"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -370,7 +369,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem remove2LeftMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_left"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_L,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -384,7 +383,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem remove2RightMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_right"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_R,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -398,7 +397,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem removeGappedColumnMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_empty_columns"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -412,8 +411,8 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem removeAllGapsMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_all_gaps"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_E,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
-                    | KeyEvent.SHIFT_MASK,
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
+                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -520,7 +519,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem removeRedundancyMenuItem = new JMenuItem(
             MessageManager.getString("action.remove_redundancy"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -695,7 +694,7 @@ public class GAlignFrame extends JInternalFrame
     undoMenuItem.setEnabled(false);
     undoMenuItem.setText(MessageManager.getString("action.undo"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -709,7 +708,7 @@ public class GAlignFrame extends JInternalFrame
     redoMenuItem.setEnabled(false);
     redoMenuItem.setText(MessageManager.getString("action.redo"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -733,7 +732,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem printMenuItem = new JMenuItem(
             MessageManager.getString("action.print"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_P,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -759,7 +758,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem findMenuItem = new JMenuItem(
             MessageManager.getString("action.find"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     findMenuItem.setToolTipText(JvSwingUtils.wrapTooltip(true,
             MessageManager.getString("label.find_tip")));
     al = new ActionListener()
@@ -920,7 +919,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem deleteGroups = new JMenuItem(
             MessageManager.getString("action.undefine_groups"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_U,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -945,7 +944,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem createGroup = new JMenuItem(
             MessageManager.getString("action.create_group"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -959,8 +958,8 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem unGroup = new JMenuItem(
             MessageManager.getString("action.remove_group"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_G,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
-                    | KeyEvent.SHIFT_MASK,
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
+                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -974,7 +973,7 @@ public class GAlignFrame extends JInternalFrame
 
     copy.setText(MessageManager.getString("action.copy"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_C,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
 
     al = new ActionListener()
     {
@@ -988,7 +987,7 @@ public class GAlignFrame extends JInternalFrame
 
     cut.setText(MessageManager.getString("action.cut"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_X,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -1014,8 +1013,8 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem pasteNew = new JMenuItem(
             MessageManager.getString("label.to_new_alignment"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
-                    | KeyEvent.SHIFT_MASK,
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
+                    | jalview.util.ShortcutKeyMaskExWrapper.SHIFT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -1030,7 +1029,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem pasteThis = new JMenuItem(
             MessageManager.getString("label.to_this_alignment"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_V,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -1547,8 +1546,8 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem invertColSel = new JMenuItem(
             MessageManager.getString("action.invert_column_selection"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_I,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
-                    | KeyEvent.ALT_MASK,
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()
+                    | jalview.util.ShortcutKeyMaskExWrapper.ALT_DOWN_MASK,
             false);
     al = new ActionListener()
     {
@@ -1611,7 +1610,7 @@ public class GAlignFrame extends JInternalFrame
 
     JMenuItem save = new JMenuItem(MessageManager.getString("action.save"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
@@ -1636,7 +1635,7 @@ public class GAlignFrame extends JInternalFrame
     JMenuItem newView = new JMenuItem(
             MessageManager.getString("action.new_view"));
     keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_T,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false);
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false);
     al = new ActionListener()
     {
       @Override
index a6e0ace..1c9e907 100644 (file)
@@ -25,7 +25,6 @@ import jalview.util.MessageManager;
 
 import java.awt.BorderLayout;
 import java.awt.Font;
-import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.MouseEvent;
@@ -136,11 +135,15 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     });
     close.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_W,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
+            jalview.util.ShortcutKeyMaskExWrapper
+                    .getMenuShortcutKeyMaskEx(),
+            false));
     selectAll.setText(MessageManager.getString("action.select_all"));
     selectAll.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_A,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
+            jalview.util.ShortcutKeyMaskExWrapper
+                    .getMenuShortcutKeyMaskEx(),
+            false));
     selectAll.addActionListener(new ActionListener()
     {
       @Override
@@ -153,7 +156,9 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     save.setText(MessageManager.getString("action.save"));
     save.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_S,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
+            jalview.util.ShortcutKeyMaskExWrapper
+                    .getMenuShortcutKeyMaskEx(),
+            false));
     save.addActionListener(new ActionListener()
     {
       @Override
@@ -164,7 +169,9 @@ public class GCutAndPasteHtmlTransfer extends JInternalFrame
     });
     copyItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_C,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
+            jalview.util.ShortcutKeyMaskExWrapper
+                    .getMenuShortcutKeyMaskEx(),
+            false));
 
     editMenubar.add(jMenu1);
     editMenubar.add(editMenu);
index 97ac840..94a4677 100755 (executable)
@@ -123,7 +123,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
     selectAll.setText(MessageManager.getString("action.select_all"));
     selectAll.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_A,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false));
     selectAll.addActionListener(new ActionListener()
     {
       @Override
@@ -136,7 +136,7 @@ public class GCutAndPasteTransfer extends JInternalFrame
     save.setText(MessageManager.getString("action.save"));
     save.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_S,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false));
     save.addActionListener(new ActionListener()
     {
       @Override
@@ -147,10 +147,10 @@ public class GCutAndPasteTransfer extends JInternalFrame
     });
     copyItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_C,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false));
     pasteMenu.setAccelerator(javax.swing.KeyStroke.getKeyStroke(
             java.awt.event.KeyEvent.VK_V,
-            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false));
+            jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(), false));
     editMenubar.add(jMenu1);
     editMenubar.add(editMenu);
     textarea.setFont(new java.awt.Font("Monospaced", Font.PLAIN, 12));
index 2ea3774..b6f3f8c 100755 (executable)
@@ -26,7 +26,6 @@ import jalview.util.MessageManager;
 import jalview.util.Platform;
 
 import java.awt.FlowLayout;
-import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 
@@ -54,10 +53,6 @@ public class GDesktop extends JFrame
 
   JMenu HelpMenu = new JMenu();
 
-  protected JMenu VamsasMenu = new JMenu();
-
-  protected JMenu VamsasStMenu = new JMenu();
-
   JMenuItem inputLocalFileMenuItem = new JMenuItem();
 
   JMenuItem inputURLMenuItem = new JMenuItem();
@@ -84,16 +79,8 @@ public class GDesktop extends JFrame
 
   JMenu inputMenu = new JMenu();
 
-  protected JMenuItem vamsasStart = new JMenuItem();
-
-  protected JMenuItem vamsasImport = new JMenuItem();
-
-  protected JMenuItem vamsasSave = new JMenuItem();
-
   JMenuItem inputSequence = new JMenuItem();
 
-  protected JMenuItem vamsasStop = new JMenuItem();
-
   JMenuItem closeAll = new JMenuItem();
 
   JMenuItem raiseRelated = new JMenuItem();
@@ -135,7 +122,6 @@ public class GDesktop extends JFrame
        // but here we want just not a Mac, period, right?
       FileMenu.setMnemonic('F');
       inputLocalFileMenuItem.setMnemonic('L');
-      VamsasMenu.setMnemonic('V'); 
       inputURLMenuItem.setMnemonic('U');
       inputTextboxMenuItem.setMnemonic('C');
       quit.setMnemonic('Q');
@@ -156,17 +142,11 @@ public class GDesktop extends JFrame
     setName("jalview-desktop");
     FileMenu.setText(MessageManager.getString("action.file"));
     HelpMenu.setText(MessageManager.getString("action.help"));
-    VamsasMenu.setText("Vamsas");
-    VamsasMenu.setToolTipText(MessageManager
-            .getString("label.share_data_vamsas_applications"));
-    VamsasStMenu.setText(MessageManager.getString("label.connect_to"));
-    VamsasStMenu.setToolTipText(
-            MessageManager.getString("label.join_existing_vamsas_session"));
     inputLocalFileMenuItem
             .setText(MessageManager.getString("label.load_tree_from_file"));
     inputLocalFileMenuItem.setAccelerator(
             javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O,
-                    Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(),
+                    jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx(),
                     false));
     inputLocalFileMenuItem
             .addActionListener(new java.awt.event.ActionListener()
@@ -274,39 +254,6 @@ public class GDesktop extends JFrame
       }
     });
     inputMenu.setText(MessageManager.getString("label.input_alignment"));
-    vamsasStart
-            .setText(MessageManager.getString("label.new_vamsas_session"));
-    vamsasStart.setVisible(false);
-    vamsasStart.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        vamsasStart_actionPerformed(e);
-      }
-    });
-    vamsasImport.setText(
-            MessageManager.getString("action.load_vamsas_session"));
-    vamsasImport.setVisible(false);
-    vamsasImport.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        vamsasImport_actionPerformed(e);
-      }
-    });
-    vamsasSave.setText(
-            MessageManager.getString("action.save_vamsas_session"));
-    vamsasSave.setVisible(false);
-    vamsasSave.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        vamsasSave_actionPerformed(e);
-      }
-    });
     inputSequence
             .setText(MessageManager.getString("action.fetch_sequences"));
     inputSequence.addActionListener(new ActionListener()
@@ -317,17 +264,6 @@ public class GDesktop extends JFrame
         inputSequence_actionPerformed(e);
       }
     });
-    vamsasStop
-            .setText(MessageManager.getString("label.stop_vamsas_session"));
-    vamsasStop.setVisible(false);
-    vamsasStop.addActionListener(new ActionListener()
-    {
-      @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        vamsasStop_actionPerformed(e);
-      }
-    });
     closeAll.setText(MessageManager.getString("action.close_all"));
     closeAll.addActionListener(new ActionListener()
     {
@@ -430,36 +366,30 @@ public class GDesktop extends JFrame
       }
     });
 
+    Float specversion = Float.parseFloat(System.getProperty("java.specification.version"));
+    
     desktopMenubar.add(FileMenu);
     desktopMenubar.add(toolsMenu);
-    VamsasMenu.setVisible(false);
-    desktopMenubar.add(VamsasMenu);
     desktopMenubar.add(HelpMenu);
     desktopMenubar.add(windowMenu);
     FileMenu.add(inputMenu);
     FileMenu.add(inputSequence);
     FileMenu.addSeparator();
-    FileMenu.add(saveState);
+    //FileMenu.add(saveState);
     FileMenu.add(saveAsState);
     FileMenu.add(loadState);
     FileMenu.addSeparator();
     FileMenu.add(quit);
     HelpMenu.add(aboutMenuItem);
     HelpMenu.add(documentationMenuItem);
-    VamsasMenu.add(VamsasStMenu);
-    VamsasStMenu.setVisible(false);
-    VamsasMenu.add(vamsasStart);
-    VamsasMenu.add(vamsasImport);
-    VamsasMenu.add(vamsasSave);
-    VamsasMenu.add(vamsasStop);
-    toolsMenu.add(preferences);
-    if (!Platform.isJS())
+    if (!Platform.isAMacAndNotJS() || specversion < 11)
     {
-      toolsMenu.add(showMemusage);
-      toolsMenu.add(showConsole);
+      toolsMenu.add(preferences);
     }
     if (!Platform.isJS())
     {
+      toolsMenu.add(showMemusage);
+      toolsMenu.add(showConsole);
       toolsMenu.add(showNews);
       toolsMenu.add(garbageCollect);
       toolsMenu.add(groovyShell);
@@ -561,6 +491,7 @@ public class GDesktop extends JFrame
    */
   protected void quit()
   {
+    //System.out.println("********** GDesktop.quit()");
   }
 
   /**
index 3c35e4e..64c0323 100755 (executable)
@@ -31,6 +31,8 @@ import jalview.gui.JvSwingUtils;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.BackupFilenameParts;
 import jalview.io.BackupFiles;
+import jalview.io.BackupFilesPresetEntry;
+import jalview.io.IntKeyStringValueEntry;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 
@@ -52,8 +54,8 @@ import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Arrays;
+import java.util.List;
 
 import javax.swing.AbstractCellEditor;
 import javax.swing.BorderFactory;
@@ -64,7 +66,6 @@ import javax.swing.JCheckBox;
 import javax.swing.JComboBox;
 import javax.swing.JFileChooser;
 import javax.swing.JLabel;
-import javax.swing.JList;
 import javax.swing.JPanel;
 import javax.swing.JRadioButton;
 import javax.swing.JScrollPane;
@@ -301,9 +302,15 @@ public class GPreferences extends JPanel
 
   protected JPanel presetsPanel = new JPanel();
 
+  protected JLabel presetsComboLabel = new JLabel();
+
+  protected JCheckBox customiseCheckbox = new JCheckBox();
+
   protected JButton revertButton = new JButton();
 
-  protected JComboBox<IntKeyStringValueEntry> backupfilesPresetsCombo = new JComboBox<>();
+  protected JComboBox<Object> backupfilesPresetsCombo = new JComboBox<>();
+
+  private int backupfilesPresetsComboLastSelected = 0;
 
   protected JPanel suffixPanel = new JPanel();
 
@@ -660,6 +667,8 @@ public class GPreferences extends JPanel
                     GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
                     new Insets(0, 2, 5, 5), 70, 1));
 
+    versioncheck.setVisible(false);
+
     // Add padding so the panel doesn't look ridiculous
     JPanel spacePanel = new JPanel();
     connectTab.add(spacePanel,
@@ -1751,22 +1760,28 @@ public class GPreferences extends JPanel
 
   protected void loadLastSavedBackupsOptions()
   {
+    BackupFilesPresetEntry savedPreset = BackupFilesPresetEntry
+            .getSavedBackupEntry();
     enableBackupFiles
             .setSelected(Cache.getDefault(BackupFiles.ENABLED, !Platform.isJS()));
+
+    BackupFilesPresetEntry backupfilesCustomEntry = BackupFilesPresetEntry
+            .createBackupFilesPresetEntry(Cache
+                    .getDefault(BackupFilesPresetEntry.CUSTOMCONFIG, null));
+    if (backupfilesCustomEntry == null)
+    {
+      backupfilesCustomEntry = BackupFilesPresetEntry.backupfilesPresetEntriesValues
+              .get(BackupFilesPresetEntry.BACKUPFILESSCHEMEDEFAULT);
+    }
+    BackupFilesPresetEntry.backupfilesPresetEntriesValues.put(
+            BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM,
+            backupfilesCustomEntry);
+
     setComboIntStringKey(backupfilesPresetsCombo,
-            Cache.getDefault(BackupFiles.NS + "_PRESET", 1));
-    suffixTemplate.setText(Cache.getDefault(BackupFiles.SUFFIX,
-            ".bak" + BackupFiles.NUM_PLACEHOLDER));
-    suffixDigitsSpinner
-            .setValue(Cache.getDefault(BackupFiles.SUFFIX_DIGITS, 3));
-    suffixReverse.setSelected(
-            Cache.getDefault(BackupFiles.REVERSE_ORDER, false));
-    backupfilesKeepAll
-            .setSelected(Cache.getDefault(BackupFiles.NO_MAX, false));
-    backupfilesRollMaxSpinner
-            .setValue(Cache.getDefault(BackupFiles.ROLL_MAX, 3));
-    backupfilesConfirmDelete.setSelected(
-            Cache.getDefault(BackupFiles.CONFIRM_DELETE_OLD, true));
+            Cache.getDefault(BackupFiles.NS + "_PRESET",
+                    BackupFilesPresetEntry.BACKUPFILESSCHEMEDEFAULT));
+
+    backupsSetOptions(savedPreset);
 
     backupsOptionsSetEnabled();
     updateBackupFilesExampleLabel();
@@ -1774,11 +1789,11 @@ public class GPreferences extends JPanel
 
   private boolean warnAboutSuffixReverseChange()
   {
-    boolean savedSuffixReverse = Cache.getDefault(BackupFiles.REVERSE_ORDER,
-            false);
-    int savedSuffixDigits = Cache.getDefault(BackupFiles.SUFFIX_DIGITS, 3);
-    String savedSuffixTemplate = Cache.getDefault(BackupFiles.SUFFIX,
-            ".bak" + BackupFiles.NUM_PLACEHOLDER);
+    BackupFilesPresetEntry bfpe = BackupFilesPresetEntry
+            .getSavedBackupEntry();
+    boolean savedSuffixReverse = bfpe.reverse;
+    int savedSuffixDigits = bfpe.digits;
+    String savedSuffixTemplate = bfpe.suffix;
 
     boolean nowSuffixReverse = suffixReverse.isSelected();
     int nowSuffixDigits = getSpinnerInt(suffixDigitsSpinner, 3);
@@ -1855,7 +1870,6 @@ public class GPreferences extends JPanel
     gbc.gridy++; // row 1
     backupsTab.add(presetsPanel, gbc);
 
-    // gbc.anchor = GridBagConstraints.NORTHWEST;
     // now using whole row
     gbc.gridwidth = 2;
     gbc.gridheight = 1;
@@ -1877,50 +1891,10 @@ public class GPreferences extends JPanel
     return backupsTab;
   }
 
-  protected static final int BACKUPFILESSCHEMECUSTOMISE = 0;
-
-  private static final IntKeyStringValueEntry[] backupfilesPresetEntries = {
-      new IntKeyStringValueEntry(1,
-              MessageManager.getString("label.default")),
-      new IntKeyStringValueEntry(2,
-              MessageManager.getString("label.single_file")),
-      new IntKeyStringValueEntry(3,
-              MessageManager.getString("label.keep_all_versions")),
-      new IntKeyStringValueEntry(4,
-              MessageManager.getString("label.rolled_backups")),
-      // ...
-      // IMPORTANT, keep "Custom" entry with key 0 (even though it appears last)
-      new IntKeyStringValueEntry(BACKUPFILESSCHEMECUSTOMISE,
-              MessageManager.getString("label.customise")) };
-
-  private static final Map<Integer, BackupFilesPresetEntry> backupfilesPresetEntriesValues = new HashMap<Integer, BackupFilesPresetEntry>()
-  {
-    /**
-     * 
-     */
-    private static final long serialVersionUID = 125L;
-
-    {
-      put(1, new BackupFilesPresetEntry(
-              ".bak" + BackupFiles.NUM_PLACEHOLDER, 3, false, false, 3,
-              false));
-      put(2, new BackupFilesPresetEntry("~", 1, false, false, 1, false));
-      put(3, new BackupFilesPresetEntry(".v" + BackupFiles.NUM_PLACEHOLDER,
-              3, false, true, 10, true));
-      put(4, new BackupFilesPresetEntry(
-              "_bak." + BackupFiles.NUM_PLACEHOLDER, 1, true, false, 9,
-              false));
-    }
-  };
-
   private JPanel initBackupsTabPresetsPanel()
   {
 
     String title = MessageManager.getString("label.schemes");
-    // TitledBorder tb = new TitledBorder(new EmptyBorder(0, 0, 0, 0), title);
-    // TitledBorder tb = new TitledBorder(title);
-    // tb.setTitleFont(LABEL_FONT);
-    // presetsPanel.setBorder(tb);
 
     presetsPanel.setLayout(new GridBagLayout());
 
@@ -1935,24 +1909,53 @@ public class GPreferences extends JPanel
     // "Scheme: "
     gbc.gridx = 0;
     gbc.gridy = 0;
-    presetsPanel.add(new JLabel(title + ":"), gbc);
 
-    for (int i = 0; i < backupfilesPresetEntries.length; i++)
+    presetsComboLabel = new JLabel(title + ":");
+    presetsPanel.add(presetsComboLabel, gbc);
+
+    List<Object> entries = Arrays
+            .asList((Object[]) BackupFilesPresetEntry.backupfilesPresetEntries);
+    List<String> tooltips = Arrays.asList(
+            BackupFilesPresetEntry.backupfilesPresetEntryDescriptions);
+    backupfilesPresetsCombo = JvSwingUtils.buildComboWithTooltips(entries,
+            tooltips);
+    /*
+    for (int i = 0; i < BackupFilesPresetEntry.backupfilesPresetEntries.length; i++)
     {
-      backupfilesPresetsCombo.addItem(backupfilesPresetEntries[i]);
+      backupfilesPresetsCombo
+              .addItem(BackupFilesPresetEntry.backupfilesPresetEntries[i]);
     }
+    */
 
-    // put "Previously saved scheme" item in italics (it's not really
-    // selectable, as such -- it deselects itself when selected) and
-    // "Customise" in bold
-    backupfilesPresetsCombo
-            .setRenderer(new BackupFilesPresetsComboBoxRenderer());
     backupfilesPresetsCombo.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        backupsTabUpdatePresets();
+        int key = getComboIntStringKey(backupfilesPresetsCombo);
+        if (!customiseCheckbox.isSelected())
+        {
+          backupfilesPresetsComboLastSelected = key;
+        }
+        if (key == BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM)
+        {
+          if (customiseCheckbox.isSelected())
+          {
+            // got here by clicking on customiseCheckbox so don't change the values
+            backupfilesCustomOptionsSetEnabled();
+          }
+          else
+          {
+            backupsTabUpdatePresets();
+            backupfilesCustomOptionsSetEnabled();
+          }
+        }
+        else
+        {
+          customiseCheckbox.setSelected(false);
+          backupsTabUpdatePresets();
+          backupfilesCustomOptionsSetEnabled();
+        }
       }
     });
 
@@ -1961,20 +1964,55 @@ public class GPreferences extends JPanel
     presetsPanel.add(backupfilesPresetsCombo, gbc);
 
     revertButton.setText(MessageManager.getString("label.cancel_changes"));
+    revertButton.setToolTipText(
+            MessageManager.getString("label.cancel_changes_description"));
     revertButton.addActionListener(new ActionListener()
     {
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        loadLastSavedBackupsOptions();
+        backupsSetOptions(
+                BackupFilesPresetEntry.backupfilesPresetEntriesValues.get(
+                        BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM));
+        backupfilesCustomOptionsSetEnabled();
       }
 
     });
     revertButton.setFont(LABEL_FONT);
 
+    customiseCheckbox.setFont(LABEL_FONT);
+    customiseCheckbox.setText(MessageManager.getString("label.customise"));
+    customiseCheckbox.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        int currently = getComboIntStringKey(backupfilesPresetsCombo);
+        if (customiseCheckbox.isSelected())
+        {
+          backupfilesPresetsComboLastSelected = currently;
+          setComboIntStringKey(backupfilesPresetsCombo,
+                  BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM);
+        }
+        else
+        {
+          setComboIntStringKey(backupfilesPresetsCombo,
+                  backupfilesPresetsComboLastSelected);
+
+        }
+        backupfilesCustomOptionsSetEnabled();
+      }
+    });
+    customiseCheckbox.setToolTipText(
+            MessageManager.getString("label.customise_description"));
+
+    // customise checkbox
+    gbc.gridx = 0;
+    gbc.gridy++;
+    presetsPanel.add(customiseCheckbox, gbc);
+
     // "Cancel changes" button (aligned with combo box above)
     gbc.gridx = 1;
-    gbc.gridy++;
     presetsPanel.add(revertButton, gbc);
 
     return presetsPanel;
@@ -1983,7 +2021,7 @@ public class GPreferences extends JPanel
   private JPanel initBackupsTabFilenameExamplesPanel()
   {
     String title = MessageManager
-            .getString("label.summary_of_backups_scheme");
+            .getString("label.scheme_examples");
     TitledBorder tb = new TitledBorder(title);
     exampleFilesPanel.setBorder(tb);
     exampleFilesPanel.setLayout(new GridBagLayout());
@@ -2009,45 +2047,68 @@ public class GPreferences extends JPanel
   {
     IntKeyStringValueEntry entry = (IntKeyStringValueEntry) backupfilesPresetsCombo
             .getSelectedItem();
-    int key = entry.getKey();
-    String value = entry.getValue();
+    int key = entry.k;
+    String value = entry.v;
 
-    // BACKUPFILESSCHEMECUSTOMISE (==0) reserved for "Custom"
-    if (key != BACKUPFILESSCHEMECUSTOMISE)
+    if (BackupFilesPresetEntry.backupfilesPresetEntriesValues
+            .containsKey(key))
     {
-      if (backupfilesPresetEntriesValues.containsKey(key))
-      {
-        backupsSetOptions(backupfilesPresetEntriesValues.get(key));
-      }
-      else
-      {
-        System.out.println("Preset '" + value + "' not implemented");
-      }
+      backupsSetOptions(
+              BackupFilesPresetEntry.backupfilesPresetEntriesValues
+                      .get(key));
+    }
+    else
+    {
+      Cache.log.error(
+              "Preset '" + value + "' [key:" + key + "] not implemented");
     }
 
-    backupfilesCustomOptionsSetEnabled();
+    // Custom options will now be enabled when the customiseCheckbox is checked
+    // (performed above)
+    // backupfilesCustomOptionsSetEnabled();
     updateBackupFilesExampleLabel();
   }
 
-  protected int getComboIntStringKey(JComboBox<IntKeyStringValueEntry> c)
+  protected int getComboIntStringKey(
+          JComboBox<Object> backupfilesPresetsCombo2)
   {
-    IntKeyStringValueEntry e = (IntKeyStringValueEntry) c.getSelectedItem();
-    return e != null ? e.getKey() : 0;
+    IntKeyStringValueEntry e;
+    try
+    {
+      e = (IntKeyStringValueEntry) backupfilesPresetsCombo2
+              .getSelectedItem();
+    } catch (Exception ex)
+    {
+      Cache.log.error(
+              "Problem casting Combo entry to IntKeyStringValueEntry.");
+      e = null;
+    }
+    return e != null ? e.k : 0;
   }
 
-  protected void setComboIntStringKey(JComboBox<IntKeyStringValueEntry> c,
+  protected void setComboIntStringKey(
+          JComboBox<Object> backupfilesPresetsCombo2,
           int key)
   {
-    for (int i = 0; i < c.getItemCount(); i++)
+    for (int i = 0; i < backupfilesPresetsCombo2.getItemCount(); i++)
     {
-      IntKeyStringValueEntry e = c.getItemAt(i);
-      if (e.getKey() == key)
+      IntKeyStringValueEntry e;
+      try
+      {
+        e = (IntKeyStringValueEntry) backupfilesPresetsCombo2.getItemAt(i);
+      } catch (Exception ex)
+      {
+        Cache.log.error(
+                "Problem casting Combo entry to IntKeyStringValueEntry. Skipping item. ");
+        continue;
+      }
+      if (e.k == key)
       {
-        c.setSelectedIndex(i);
+        backupfilesPresetsCombo2.setSelectedIndex(i);
         break;
       }
     }
-    backupsTabUpdatePresets();
+    // backupsTabUpdatePresets();
   }
 
   private JPanel initBackupsTabSuffixPanel()
@@ -2071,16 +2132,18 @@ public class GPreferences extends JPanel
       {
         updateBackupFilesExampleLabel();
         backupfilesCustomOptionsSetEnabled();
+        backupfilesRevertButtonSetEnabled(true);
       }
 
     });
-    KeyListener kl = new KeyListener()
+    suffixTemplate.addKeyListener(new KeyListener()
     {
       @Override
       public void keyReleased(KeyEvent e)
       {
         updateBackupFilesExampleLabel();
         backupfilesCustomOptionsSetEnabled();
+        backupfilesRevertButtonSetEnabled(true);
       }
 
       @Override
@@ -2100,26 +2163,25 @@ public class GPreferences extends JPanel
         }
       }
 
-    };
-    suffixTemplate.addKeyListener(kl);
+    });
 
     // digits spinner
     suffixDigitsLabel
             .setText(MessageManager.getString("label.index_digits"));
     suffixDigitsLabel.setHorizontalAlignment(SwingConstants.LEFT);
     suffixDigitsLabel.setFont(LABEL_FONT);
-    int defaultmin = 1;
-    int defaultmax = 6;
     ChangeListener c = new ChangeListener()
     {
       @Override
       public void stateChanged(ChangeEvent e)
       {
+        backupfilesRevertButtonSetEnabled(true);
         updateBackupFilesExampleLabel();
       }
 
     };
-    setIntegerSpinner(suffixDigitsSpinner, defaultmin, defaultmax, 3, c);
+    setIntegerSpinner(suffixDigitsSpinner, BackupFilesPresetEntry.DIGITSMIN,
+            BackupFilesPresetEntry.DIGITSMAX, 3, c);
 
     suffixReverse.setLabels(MessageManager.getString("label.reverse_roll"),
             MessageManager.getString("label.increment_index"));
@@ -2136,12 +2198,13 @@ public class GPreferences extends JPanel
         }
         if (okay)
         {
+          backupfilesRevertButtonSetEnabled(true);
           updateBackupFilesExampleLabel();
         }
         else
         {
-          boolean savedSuffixReverse = Cache
-                  .getDefault(BackupFiles.REVERSE_ORDER, false);
+          boolean savedSuffixReverse = BackupFilesPresetEntry
+                  .getSavedBackupEntry().reverse;
           suffixReverse.setSelected(savedSuffixReverse);
         }
       }
@@ -2217,6 +2280,7 @@ public class GPreferences extends JPanel
       @Override
       public void actionPerformed(ActionEvent e)
       {
+        backupfilesRevertButtonSetEnabled(true);
         updateBackupFilesExampleLabel();
       }
     });
@@ -2225,6 +2289,7 @@ public class GPreferences extends JPanel
       @Override
       public void actionPerformed(ActionEvent e)
       {
+        backupfilesRevertButtonSetEnabled(true);
         keepRollMaxOptionsEnabled();
         updateBackupFilesExampleLabel();
       }
@@ -2235,15 +2300,26 @@ public class GPreferences extends JPanel
       @Override
       public void stateChanged(ChangeEvent e)
       {
+        backupfilesRevertButtonSetEnabled(true);
         updateBackupFilesExampleLabel();
       }
 
     };
-    setIntegerSpinner(backupfilesRollMaxSpinner, 1, 999, 4, true, c);
+    setIntegerSpinner(backupfilesRollMaxSpinner,
+            BackupFilesPresetEntry.ROLLMAXMIN,
+            BackupFilesPresetEntry.ROLLMAXMAX, 4, true, c);
 
     backupfilesConfirmDelete.setLabels(
             MessageManager.getString("label.always_ask"),
             MessageManager.getString("label.auto_delete"));
+    backupfilesConfirmDelete.addActionListener(new ActionListener()
+    {
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        backupfilesRevertButtonSetEnabled(true);
+      }
+    });
     // update the enabled section
     keepRollMaxOptionsEnabled();
 
@@ -2279,13 +2355,6 @@ public class GPreferences extends JPanel
     kgbc.gridwidth = GridBagConstraints.REMAINDER;
     kgbc.fill = GridBagConstraints.HORIZONTAL;
     kgbc.weightx = 1.0;
-    /*
-    keepfilesPanel.add(backupfilesConfirmDelete.getTrueButton(), kgbc);
-    
-    // fourth row (indented)
-    kgbc.gridy = 3;
-    keepfilesPanel.add(backupfilesConfirmDelete.getFalseButton(), kgbc);
-    */
 
     JPanel jp = new JPanel();
     jp.setLayout(new FlowLayout());
@@ -2341,6 +2410,7 @@ public class GPreferences extends JPanel
     int uppersurround = 0;
     StringBuilder exampleSB = new StringBuilder();
     boolean firstLine = true;
+    int lineNumber = 0;
     if (reverse)
     {
 
@@ -2351,6 +2421,7 @@ public class GPreferences extends JPanel
         if (index == min + lowersurround && index < max - uppersurround - 1)
         {
           exampleSB.append("\n...");
+          lineNumber++;
         }
         else if (index > min + lowersurround && index < max - uppersurround)
         {
@@ -2365,6 +2436,7 @@ public class GPreferences extends JPanel
           else
           {
             exampleSB.append("\n");
+            lineNumber++;
           }
           exampleSB.append(BackupFilenameParts.getBackupFilename(index,
                   base, suffix, digits));
@@ -2404,6 +2476,7 @@ public class GPreferences extends JPanel
         if (index == min + lowersurround && index < max - uppersurround - 1)
         {
           exampleSB.append("\n...");
+          lineNumber++;
         }
         else if (index > min + lowersurround && index < max - uppersurround)
         {
@@ -2418,6 +2491,7 @@ public class GPreferences extends JPanel
           else
           {
             exampleSB.append("\n");
+            lineNumber++;
           }
           exampleSB.append(BackupFilenameParts.getBackupFilename(index,
                   base, suffix, digits));
@@ -2446,6 +2520,18 @@ public class GPreferences extends JPanel
 
     }
 
+    // add some extra empty lines to pad out the example files box. ugh, please tell
+    // me how to do this better
+    int remainingLines = lowersurround + uppersurround + 1 - lineNumber;
+    if (remainingLines > 0)
+    {
+      for (int i = 0; i < remainingLines; i++)
+      {
+        exampleSB.append("\n ");
+        lineNumber++;
+      }
+    }
+
     backupfilesExampleLabel.setText(exampleSB.toString());
   }
 
@@ -2460,7 +2546,7 @@ public class GPreferences extends JPanel
         i = Integer.parseInt((String) s.getValue());
       } catch (Exception e)
       {
-        System.out.println(
+        Cache.log.error(
                 "Exception casting the initial value of s.getValue()");
       }
     }
@@ -2492,7 +2578,7 @@ public class GPreferences extends JPanel
       i = (Integer) s.getValue();
     } catch (Exception e)
     {
-      System.out.println("Failed casting (Integer) JSpinner s.getValue()");
+      Cache.log.error("Failed casting (Integer) JSpinner s.getValue()");
     }
     return i;
   }
@@ -2509,7 +2595,7 @@ public class GPreferences extends JPanel
   private void backupfilesKeepAllSetEnabled(boolean tryEnabled)
   {
     boolean enabled = tryEnabled && enableBackupFiles.isSelected()
-            && getComboIntStringKey(backupfilesPresetsCombo) == 0
+            && customiseCheckbox.isSelected()
             && suffixTemplate.getText()
                     .indexOf(BackupFiles.NUM_PLACEHOLDER) > -1;
     keepfilesPanel.setEnabled(enabled);
@@ -2530,18 +2616,48 @@ public class GPreferences extends JPanel
   private void backupfilesSuffixTemplateSetEnabled(boolean tryEnabled)
   {
     boolean enabled = tryEnabled && enableBackupFiles.isSelected()
-            && getComboIntStringKey(backupfilesPresetsCombo) == 0;
+            && customiseCheckbox.isSelected();
     suffixPanel.setEnabled(enabled);
     suffixTemplateLabel.setEnabled(enabled);
     suffixTemplate.setEnabled(enabled);
     backupfilesSuffixTemplateDigitsSetEnabled();
   }
 
+  private void backupfilesRevertButtonSetEnabled(boolean tryEnabled)
+  {
+    boolean enabled = tryEnabled && enableBackupFiles.isSelected()
+            && customiseCheckbox.isSelected() && backupfilesCustomChanged();
+    revertButton.setEnabled(enabled);
+  }
+
+  private boolean backupfilesCustomChanged()
+  {
+    BackupFilesPresetEntry custom = BackupFilesPresetEntry.backupfilesPresetEntriesValues
+            .get(BackupFilesPresetEntry.BACKUPFILESSCHEMECUSTOM);
+    BackupFilesPresetEntry current = getBackupfilesCurrentEntry();
+    return !custom.equals(current);
+  }
+
+  protected BackupFilesPresetEntry getBackupfilesCurrentEntry()
+  {
+    String suffix = suffixTemplate.getText();
+    int digits = getSpinnerInt(suffixDigitsSpinner, 3);
+    boolean reverse = suffixReverse.isSelected();
+    boolean keepAll = backupfilesKeepAll.isSelected();
+    int rollMax = getSpinnerInt(backupfilesRollMaxSpinner, 3);
+    boolean confirmDelete = backupfilesConfirmDelete.isSelected();
+
+    BackupFilesPresetEntry bfpe = new BackupFilesPresetEntry(suffix, digits,
+            reverse, keepAll, rollMax, confirmDelete);
+
+    return bfpe;
+  }
+
   protected void backupfilesCustomOptionsSetEnabled()
   {
-    int scheme = getComboIntStringKey(backupfilesPresetsCombo);
-    boolean enabled = scheme == 0 && enableBackupFiles.isSelected();
+    boolean enabled = customiseCheckbox.isSelected();
 
+    backupfilesRevertButtonSetEnabled(enabled);
     backupfilesSuffixTemplateSetEnabled(enabled);
     backupfilesKeepAllSetEnabled(enabled);
   }
@@ -2557,7 +2673,10 @@ public class GPreferences extends JPanel
   {
     boolean enabled = enableBackupFiles.isSelected();
     presetsPanel.setEnabled(enabled);
+    presetsComboLabel.setEnabled(enabled);
     backupfilesPresetsCombo.setEnabled(enabled);
+    customiseCheckbox.setEnabled(enabled);
+    revertButton.setEnabled(enabled);
   }
 
   protected void backupsOptionsSetEnabled()
@@ -2771,85 +2890,3 @@ public class GPreferences extends JPanel
   }
 }
 
-class IntKeyStringValueEntry
-{
-  int k;
-
-  String v;
-
-  public IntKeyStringValueEntry(int k, String v)
-  {
-    this.k = k;
-    this.v = v;
-  }
-
-  @Override
-  public String toString()
-  {
-    return this.getValue();
-  }
-
-  public int getKey()
-  {
-    return k;
-  }
-
-  public String getValue()
-  {
-    return v;
-  }
-}
-
-class BackupFilesPresetEntry
-{
-  String suffix;
-
-  int digits;
-
-  boolean reverse;
-
-  boolean keepAll;
-
-  int rollMax;
-
-  boolean confirmDelete;
-
-  public BackupFilesPresetEntry(String suffix, int digits, boolean reverse,
-          boolean keepAll, int rollMax, boolean confirmDelete)
-  {
-    this.suffix = suffix;
-    this.digits = digits;
-    this.reverse = reverse;
-    this.keepAll = keepAll;
-    this.rollMax = rollMax;
-    this.confirmDelete = confirmDelete;
-  }
-}
-
-class BackupFilesPresetsComboBoxRenderer extends DefaultListCellRenderer
-{
-  /**
-   * 
-   */
-  private static final long serialVersionUID = 88L;
-
-  @Override
-  public Component getListCellRendererComponent(JList list, Object value,
-          int index, boolean isSelected, boolean cellHasFocus)
-  {
-    super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
-    
-    try {
-      IntKeyStringValueEntry e = (IntKeyStringValueEntry) value;
-      if (e.getKey() == GPreferences.BACKUPFILESSCHEMECUSTOMISE)
-      {
-        // "Customise" item
-        this.setFont(this.getFont().deriveFont(Font.BOLD));
-      }
-    } catch (Exception e) {
-      return this;
-    }
-
-    return this;
-  }
-}
index ba84411..cddbb3b 100755 (executable)
@@ -25,6 +25,7 @@ import jalview.util.MessageManager;
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
+import java.awt.Font;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
 import java.awt.GridLayout;
@@ -61,6 +62,8 @@ public class GWebserviceInfo extends JPanel
 
   protected JPanel buttonPanel = new JPanel();
 
+  public JLabel titleText = new JLabel();
+
   public JButton cancel = new JButton();
 
   public JButton showResultsNewFrame = new JButton();
@@ -95,7 +98,7 @@ public class GWebserviceInfo extends JPanel
    */
   private void jbInit() throws Exception
   {
-    infoText.setFont(new java.awt.Font("Verdana", 0, 10));
+    infoText.setFont(new Font("Verdana", 0, 10));
     infoText.setBorder(null);
     infoText.setEditable(false);
     infoText.setText("");
@@ -106,13 +109,17 @@ public class GWebserviceInfo extends JPanel
     titlePanel.setBackground(Color.white);
     titlePanel.setPreferredSize(new Dimension(0, 60));
     titlePanel.setLayout(borderLayout3);
+    titleText.setFont(new Font("Arial", Font.BOLD, 12));
+    titleText.setBorder(null);
+    titleText.setText("");
     jScrollPane1.setBorder(null);
     jScrollPane1.setPreferredSize(new Dimension(400, 70));
-    cancel.setFont(new java.awt.Font("Verdana", 0, 11));
+    cancel.setFont(new Font("Verdana", 0, 11));
     cancel.setText(MessageManager.getString("action.cancel"));
     cancel.addActionListener(new java.awt.event.ActionListener()
     {
-      public void actionPerformed(ActionEvent e)
+      @Override
+       public void actionPerformed(ActionEvent e)
       {
         cancel_actionPerformed(e);
       }
index 4d772a1..8f9338d 100644 (file)
@@ -39,6 +39,7 @@ import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.GeneLocus;
 import jalview.datamodel.GraphLine;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Point;
@@ -2520,6 +2521,10 @@ public class Jalview2XML
         parentseq = jds;
       }
     }
+
+    /*
+     * save any dbrefs; special subclass GeneLocus is flagged as 'locus'
+     */
     if (dbrefs != null)
     {
       for (int d = 0, nd = dbrefs.size(); d < nd; d++)
@@ -2529,13 +2534,16 @@ public class Jalview2XML
         dbref.setSource(ref.getSource());
         dbref.setVersion(ref.getVersion());
         dbref.setAccessionId(ref.getAccessionId());
+        if (ref instanceof GeneLocus)
+        {
+          dbref.setLocus(true);
+        }
         if (ref.hasMap())
         {
           Mapping mp = createVamsasMapping(ref.getMap(), parentseq,
                   jds, recurse);
           dbref.setMapping(mp);
         }
-        // vamsasSeq.addDBRef(dbref);
         vamsasSeq.getDBRef().add(dbref);
       }
     }
@@ -4536,7 +4544,7 @@ public class Jalview2XML
           String reformatedOldFilename = oldfilenam.replaceAll("/", "\\\\");
           filedat = oldFiles.get(new File(reformatedOldFilename));
         }
-        newFileLoc.append(Platform.escapeString(filedat.getFilePath()));
+        newFileLoc.append(Platform.escapeBackslashes(filedat.getFilePath()));
         pdbfilenames.add(filedat.getFilePath());
         pdbids.add(filedat.getPdbId());
         seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
@@ -5140,7 +5148,7 @@ public class Jalview2XML
         }
         else
         {
-          featureOrder.put(featureType, new Float(
+          featureOrder.put(featureType, Float.valueOf(
                   fs / jm.getFeatureSettings().getSetting().size()));
         }
         if (safeBoolean(setting.isDisplay()))
@@ -5152,7 +5160,7 @@ public class Jalview2XML
       for (int gs = 0; gs < jm.getFeatureSettings().getGroup().size(); gs++)
       {
         Group grp = jm.getFeatureSettings().getGroup().get(gs);
-        fgtable.put(grp.getName(), new Boolean(grp.isDisplay()));
+        fgtable.put(grp.getName(), Boolean.valueOf(grp.isDisplay()));
       }
       // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
       // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ?
@@ -5825,13 +5833,29 @@ public class Jalview2XML
     return datasetId;
   }
 
+  /**
+   * Add any saved DBRefEntry's to the sequence. An entry flagged as 'locus' is
+   * constructed as a special subclass GeneLocus.
+   * 
+   * @param datasetSequence
+   * @param sequence
+   */
   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
   {
     for (int d = 0; d < sequence.getDBRef().size(); d++)
     {
       DBRef dr = sequence.getDBRef().get(d);
-      jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
-              dr.getSource(), dr.getVersion(), dr.getAccessionId());
+      DBRefEntry entry;
+      if (dr.isLocus())
+      {
+        entry = new GeneLocus(dr.getSource(), dr.getVersion(),
+                dr.getAccessionId());
+      }
+      else
+      {
+        entry = new DBRefEntry(dr.getSource(), dr.getVersion(),
+                dr.getAccessionId());
+      }
       if (dr.getMapping() != null)
       {
         entry.setMap(addMapping(dr.getMapping()));
index 42465f2..d31fbba 100644 (file)
@@ -72,12 +72,16 @@ public class ColourSchemes
     {
       try
       {
-        registerColourScheme(cs.getSchemeClass().newInstance());
+        registerColourScheme(
+                cs.getSchemeClass().getDeclaredConstructor().newInstance());
       } catch (InstantiationException | IllegalAccessException e)
       {
         System.err.println("Error instantiating colour scheme for "
                 + cs.toString() + " " + e.getMessage());
         e.printStackTrace();
+      } catch (ReflectiveOperationException roe)
+      {
+        roe.printStackTrace();
       }
     }
   }
index f0d1990..9d2c738 100755 (executable)
@@ -53,8 +53,10 @@ public class Consensus
   /**
    * @deprecated Use {@link #isConserved(int[][],int,int,boolean)} instead
    */
+  @Deprecated
   public boolean isConserved(int[][] cons2, int col, int size)
   {
+    System.out.println("DEPRECATED!!!!");
     return isConserved(cons2, col, size, true);
   }
 
@@ -71,7 +73,7 @@ public class Consensus
       tot += cons2[col][mask[i]];
     }
 
-    if ((double) tot > ((threshold * size) / 100))
+    if (tot > ((threshold * size) / 100))
     {
       // System.out.println("True conserved "+tot+" from "+threshold+" out of
       // "+size+" : "+maskstr);
index 6483b85..0d36f4f 100644 (file)
@@ -323,7 +323,7 @@ public class FeatureColour implements FeatureColourI
       {
         if (minval.length() > 0)
         {
-          min = new Float(minval).floatValue();
+          min = Float.valueOf(minval).floatValue();
         }
       } catch (Exception e)
       {
@@ -335,7 +335,7 @@ public class FeatureColour implements FeatureColourI
       {
         if (maxval.length() > 0)
         {
-          max = new Float(maxval).floatValue();
+          max = Float.valueOf(maxval).floatValue();
         }
       } catch (Exception e)
       {
@@ -403,7 +403,7 @@ public class FeatureColour implements FeatureColourI
         {
           gcol.nextToken();
           tval = gcol.nextToken();
-          featureColour.setThreshold(new Float(tval).floatValue());
+          featureColour.setThreshold(Float.valueOf(tval).floatValue());
         } catch (Exception e)
         {
           System.err.println("Couldn't parse threshold value as a float: ("
index 6261e74..1744467 100644 (file)
@@ -41,6 +41,7 @@ import jalview.io.DataSourceType;
 import jalview.io.StructureFile;
 import jalview.util.MappingUtils;
 import jalview.util.MessageManager;
+import jalview.util.Platform;
 import jalview.ws.sifts.SiftsClient;
 import jalview.ws.sifts.SiftsException;
 import jalview.ws.sifts.SiftsSettings;
@@ -1170,7 +1171,8 @@ public class StructureSelectionManager
     StringBuilder sb = new StringBuilder(64);
     for (StructureMapping sm : mappings)
     {
-      if (sm.pdbfile.equals(pdbfile) && seqs.contains(sm.sequence))
+      if (Platform.pathEquals(sm.pdbfile, pdbfile)
+              && seqs.contains(sm.sequence))
       {
         sb.append(sm.mappingDetails);
         sb.append(NEWLINE);
@@ -1343,7 +1345,10 @@ public class StructureSelectionManager
         instances.remove(jalviewLite);
         try
         {
-          mnger.finalize();
+          /* bsoares 2019-03-20 finalize deprecated, no apparent external
+           * resources to close
+           */
+          // mnger.finalize();
         } catch (Throwable x)
         {
         }
index 162e79f..e89596b 100755 (executable)
@@ -119,17 +119,18 @@ public class ImageMaker
     this.type = imageType;
 
     out = new FileOutputStream(file);
-    if (imageType == TYPE.SVG)
+    switch (imageType)
     {
+    case SVG:
       setupSVG(width, height, useLineart);
-    }
-    else if (imageType == TYPE.EPS)
-    {
+      break;
+    case EPS:
       setupEPS(width, height, fileTitle, useLineart);
-    }
-    else if (imageType == TYPE.PNG)
-    {
+      break;
+    case PNG:
       setupPNG(width, height);
+      break;
+    default:
     }
   }
 
index f9eb2a6..ade9d87 100644 (file)
@@ -135,23 +135,15 @@ public class Platform
   }
 
   /**
-   * escape a string according to the local platform's escape character
+   * Answers the input with every backslash replaced with a double backslash (an
+   * 'escaped' single backslash)
    * 
-   * @param file
-   * @return escaped file
+   * @param s
+   * @return
    */
-  public static String escapeString(String file)
+  public static String escapeBackslashes(String s)
   {
-    StringBuffer f = new StringBuffer();
-    int p = 0, lastp = 0;
-    while ((p = file.indexOf('\\', lastp)) > -1)
-    {
-      f.append(file.subSequence(lastp, p));
-      f.append("\\\\");
-      lastp = p + 1;
-    }
-    f.append(file.substring(lastp));
-    return f.toString();
+    return s == null ? null : s.replace("\\", "\\\\");
   }
 
   /**
@@ -180,6 +172,20 @@ public class Platform
     if (!aMac)
     {
       return e.isControlDown();
+
+      // Jalview 2.11 code below: above is as amended for JalviewJS
+      // /*
+      // * answer false for right mouse button
+      // */
+      // if (e.isPopupTrigger())
+      // {
+      // return false;
+      // }
+      // return
+      // (jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx() //
+      // .getMenuShortcutKeyMaskEx()
+      // & jalview.util.ShortcutKeyMaskExWrapper
+      // .getModifiersEx(e)) != 0; // getModifiers()) != 0;
     }
     // answer false for right mouse button
     // shortcut key will be META for a Mac
@@ -611,4 +617,26 @@ public class Platform
 
   }
 
+  /**
+   * A (case sensitive) file path comparator that ignores the difference between /
+   * and \
+   * 
+   * @param path1
+   * @param path2
+   * @return
+   */
+  public static boolean pathEquals(String path1, String path2)
+  {
+    if (path1 == null)
+    {
+      return path2 == null;
+    }
+    if (path2 == null)
+    {
+      return false;
+    }
+    String p1 = path1.replace('\\', '/');
+    String p2 = path2.replace('\\', '/');
+    return p1.equals(p2);
+  }
 }
diff --git a/src/jalview/util/ShortcutKeyMaskExWrapper.java b/src/jalview/util/ShortcutKeyMaskExWrapper.java
new file mode 100644 (file)
index 0000000..f73b8d4
--- /dev/null
@@ -0,0 +1,48 @@
+package jalview.util;
+
+import java.awt.event.MouseEvent;
+
+public class ShortcutKeyMaskExWrapper
+{
+
+  private static final Float specversion;
+
+  private static final float modern;
+
+  public static final int SHIFT_DOWN_MASK;
+
+  public static final int ALT_DOWN_MASK;
+
+  private static final ShortcutKeyMaskExWrapperI wrapper;
+
+  static
+  {
+    specversion = Float
+            .parseFloat(System.getProperty("java.specification.version"));
+    modern = 11;
+
+    if (specversion >= modern)
+    {
+      wrapper = new jalview.util.ShortcutKeyMaskExWrapper11();
+      SHIFT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper11.SHIFT_DOWN_MASK;
+      ALT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper11.ALT_DOWN_MASK;
+    }
+    else
+    {
+      wrapper = new jalview.util.ShortcutKeyMaskExWrapper8();
+      SHIFT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper8.SHIFT_DOWN_MASK;
+      ALT_DOWN_MASK = jalview.util.ShortcutKeyMaskExWrapper8.ALT_DOWN_MASK;
+    }
+  }
+
+  public static int getMenuShortcutKeyMaskEx()
+  {
+    return wrapper.getMenuShortcutKeyMaskEx();
+  }
+
+  public static int getModifiersEx(MouseEvent e)
+  {
+    return wrapper.getModifiersEx(e);
+  }
+
+}
diff --git a/src/jalview/util/ShortcutKeyMaskExWrapper11.java b/src/jalview/util/ShortcutKeyMaskExWrapper11.java
new file mode 100644 (file)
index 0000000..7a3c8ca
--- /dev/null
@@ -0,0 +1,42 @@
+package jalview.util;
+
+import java.awt.GraphicsEnvironment;
+import java.awt.Toolkit;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+public class ShortcutKeyMaskExWrapper11 implements ShortcutKeyMaskExWrapperI
+{
+  public final static int SHIFT_DOWN_MASK;
+
+  public final static int ALT_DOWN_MASK;
+
+  static
+  {
+    SHIFT_DOWN_MASK = KeyEvent.SHIFT_DOWN_MASK;
+    ALT_DOWN_MASK = KeyEvent.ALT_DOWN_MASK;
+  }
+
+  @Override
+  public int getMenuShortcutKeyMaskEx()
+  {
+    try
+    {
+      if (!GraphicsEnvironment.isHeadless())
+      {
+        return Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
+      }
+    } catch (java.lang.Throwable t)
+    {
+    }
+    return 0;
+  }
+
+  @Override
+  public int getModifiersEx(MouseEvent e)
+  {
+    return e.getModifiersEx();
+  }
+
+
+}
diff --git a/src/jalview/util/ShortcutKeyMaskExWrapper8.java b/src/jalview/util/ShortcutKeyMaskExWrapper8.java
new file mode 100644 (file)
index 0000000..a7817bd
--- /dev/null
@@ -0,0 +1,41 @@
+package jalview.util;
+
+import java.awt.GraphicsEnvironment;
+import java.awt.Toolkit;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+public class ShortcutKeyMaskExWrapper8 implements ShortcutKeyMaskExWrapperI
+{
+  public final static int SHIFT_DOWN_MASK;
+
+  public final static int ALT_DOWN_MASK;
+
+  static
+  {
+    SHIFT_DOWN_MASK = KeyEvent.SHIFT_MASK;
+    ALT_DOWN_MASK = KeyEvent.ALT_MASK;
+  }
+
+  @Override
+  public int getMenuShortcutKeyMaskEx()
+  {
+    try
+    {
+      if (!GraphicsEnvironment.isHeadless())
+      {
+        return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
+      }
+    } catch (java.lang.Throwable t)
+    {
+    }
+    return 0;
+  }
+
+  @Override
+  public int getModifiersEx(MouseEvent e)
+  {
+    return e.getModifiers();
+  }
+
+}
diff --git a/src/jalview/util/ShortcutKeyMaskExWrapperI.java b/src/jalview/util/ShortcutKeyMaskExWrapperI.java
new file mode 100644 (file)
index 0000000..bf17259
--- /dev/null
@@ -0,0 +1,14 @@
+package jalview.util;
+
+import java.awt.event.MouseEvent;
+
+public interface ShortcutKeyMaskExWrapperI
+{
+  public static int SHIFT_DOWN_MASK = 0;
+
+  public static int ALT_DOWN_MASK = 0;
+
+  public int getMenuShortcutKeyMaskEx();
+
+  public int getModifiersEx(MouseEvent e);
+}
index 6bf1f45..54bb4ee 100644 (file)
@@ -653,7 +653,7 @@ public abstract class FeatureRendererModel
     {
       featureOrder = new Hashtable<>();
     }
-    featureOrder.put(type, new Float(position));
+    featureOrder.put(type, Float.valueOf(position));
     return position;
   }
 
@@ -853,7 +853,7 @@ public abstract class FeatureRendererModel
     }
     if (newGroupsVisible)
     {
-      featureGroups.put(group, new Boolean(true));
+      featureGroups.put(group, Boolean.valueOf(true));
       return true;
     }
     return false;
@@ -889,7 +889,7 @@ public abstract class FeatureRendererModel
   @Override
   public void setGroupVisibility(String group, boolean visible)
   {
-    featureGroups.put(group, new Boolean(visible));
+    featureGroups.put(group, Boolean.valueOf(visible));
   }
 
   @Override
@@ -901,7 +901,7 @@ public abstract class FeatureRendererModel
       for (String gst : toset)
       {
         Boolean st = featureGroups.get(gst);
-        featureGroups.put(gst, new Boolean(visible));
+        featureGroups.put(gst, Boolean.valueOf(visible));
         if (st != null)
         {
           rdrw = rdrw || (visible != st.booleanValue());
index b1c987e..6b8843d 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.ws;
 
 import jalview.analysis.AlignSeq;
+import jalview.api.FeatureSettingsModelI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
@@ -39,8 +40,10 @@ import jalview.ws.seqfetcher.DbSourceProxy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
@@ -317,6 +320,9 @@ public class DBRefFetcher implements Runnable
             Arrays.asList(dataset));
     List<String> warningMessages = new ArrayList<>();
 
+    // clear any old feature display settings recorded from past sessions
+    featureDisplaySettings = null;
+
     int db = 0;
     while (sdataset.size() > 0 && db < dbSources.length)
     {
@@ -383,7 +389,7 @@ public class DBRefFetcher implements Runnable
           }
           if (retrieved != null)
           {
-            transferReferences(sdataset, dbsource.getDbSource(), retrieved,
+            transferReferences(sdataset, dbsource, retrieved,
                     trimDsSeqs, warningMessages);
           }
         }
@@ -512,7 +518,8 @@ public class DBRefFetcher implements Runnable
    * @param warningMessages
    *          a list of messages to add to
    */
-  boolean transferReferences(Vector<SequenceI> sdataset, String dbSource,
+  boolean transferReferences(Vector<SequenceI> sdataset,
+          DbSourceProxy dbSourceProxy,
           AlignmentI retrievedAl, boolean trimDatasetSeqs,
           List<String> warningMessages)
   {
@@ -522,6 +529,7 @@ public class DBRefFetcher implements Runnable
       return false;
     }
 
+    String dbSource = dbSourceProxy.getDbName();
     boolean modified = false;
     SequenceI[] retrieved = recoverDbSequences(
             retrievedAl.getSequencesArray());
@@ -593,6 +601,10 @@ public class DBRefFetcher implements Runnable
        * seqs.elementAt(jj); if (!sequenceMatches.contains(sequence)) {
        * sequenceMatches.addElement(sequence); } } } }
        */
+      if (sequenceMatches.size() > 0)
+      {
+        addFeatureSettings(dbSourceProxy);
+      }
       // sequenceMatches now contains the set of all sequences associated with
       // the returned db record
       final String retrievedSeqString = retrievedSeq.getSequenceAsString();
@@ -761,6 +773,33 @@ public class DBRefFetcher implements Runnable
     return modified;
   }
 
+  Map<String, FeatureSettingsModelI> featureDisplaySettings = null;
+
+  private void addFeatureSettings(DbSourceProxy dbSourceProxy)
+  {
+    FeatureSettingsModelI fsettings = dbSourceProxy
+            .getFeatureColourScheme();
+    if (fsettings != null)
+    {
+      if (featureDisplaySettings == null)
+      {
+        featureDisplaySettings = new HashMap<>();
+      }
+      featureDisplaySettings.put(dbSourceProxy.getDbName(), fsettings);
+    }
+  }
+
+  /**
+   * 
+   * @return any feature settings associated with sources that have provided sequences
+   */
+  public List<FeatureSettingsModelI>getFeatureSettingsModels()
+  {
+    return featureDisplaySettings == null
+            ? Arrays.asList(new FeatureSettingsModelI[0])
+            : Arrays.asList(featureDisplaySettings.values()
+                    .toArray(new FeatureSettingsModelI[1]));
+  }
   /**
    * Adds the message to the list unless it already contains it
    * 
index 18e25cc..a480176 100644 (file)
@@ -86,6 +86,7 @@ public class SequenceFetcher extends ASequenceFetcher
         src.add(srcs[i]);
       }
     }
+
     Collections.sort(src, String.CASE_INSENSITIVE_ORDER);
     return src.toArray(new String[src.size()]);
   }
index 80c9ce9..f9e597f 100644 (file)
@@ -237,7 +237,7 @@ public class RNAalifoldClient extends JabawsCalcWorker
         // The Score objects contain a set of size one containing the range and
         // an ArrayList<float> of size one containing the probabilty
         basePairs.put(score.getRanges().first(),
-                new Float(score.getScores().get(0)));
+                Float.valueOf(score.getScores().get(0)));
       }
 
       for (int i = 0, ri = 0, iEnd = struct.length(); i < iEnd; i++, ri++)
index 2f3c298..e092192 100644 (file)
@@ -38,7 +38,7 @@ import compbio.data.msa.SequenceAnnotation;
 import compbio.metadata.PresetManager;
 import compbio.metadata.RunnerConfig;
 
-public class Jws2Instance
+public class Jws2Instance implements AutoCloseable
 {
   public String hosturl;
 
@@ -164,7 +164,7 @@ public class Jws2Instance
   }
 
   @Override
-  protected void finalize() throws Throwable
+  public void close()
   {
     if (service != null)
     {
@@ -176,7 +176,7 @@ public class Jws2Instance
         // ignore
       }
     }
-    super.finalize();
+    // super.finalize();
   }
 
   public ParamDatastoreI getParamStore()
index 4d5a2aa..5bfe3b5 100644 (file)
@@ -50,7 +50,7 @@ import org.apache.james.mime4j.parser.MimeStreamParser;
  * 
  */
 
-public class HttpResultSet extends FileParse
+public class HttpResultSet extends FileParse implements AutoCloseable
 {
 
   private HttpRequestBase cachedRequest;
@@ -89,7 +89,7 @@ public class HttpResultSet extends FileParse
    */
   public List<DataProvider> createResultDataProviders()
   {
-    List<DataProvider> dp = new ArrayList<DataProvider>();
+    List<DataProvider> dp = new ArrayList<>();
     for (JvDataType type : restJob.rsd.getResultDataTypes())
     {
       dp.add(new SimpleDataProvider(type, this, null));
@@ -106,7 +106,7 @@ public class HttpResultSet extends FileParse
    */
   public Object[] parseResultSet() throws Exception, Error
   {
-    List<DataProvider> dp = new ArrayList<DataProvider>();
+    List<DataProvider> dp = new ArrayList<>();
     Object[] results = null;
 
     if (en == null)
@@ -200,7 +200,7 @@ public class HttpResultSet extends FileParse
   }
 
   @Override
-  protected void finalize() throws Throwable
+  public void close()
   {
     dataIn = null;
     cachedRequest = null;
@@ -215,7 +215,8 @@ public class HttpResultSet extends FileParse
     } catch (Error ex)
     {
     }
-    super.finalize();
+    // no finalize for FileParse
+    // super.close();
   }
 
   /**
index 83f1ee2..cb90984 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 0dbcad0..35f78f3 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 44affa2..6f5ef65 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index de408d2..6780323 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index b4c07bc..5edb2e8 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index e2592ab..68ebf9b 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 9001ee2..39fc0c3 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 0daf56a..c92f72c 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index bf69d5b..0c21215 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 5684acf..36b454f 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index ed57edc..0ea2491 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
@@ -208,6 +208,8 @@ import javax.xml.datatype.XMLGregorianCalendar;
  *                   &lt;attribute name="textColThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
  *                   &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" />
  *                   &lt;attribute name="complementId" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                   &lt;attribute name="showComplementFeatures" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                   &lt;attribute name="showComplementFeaturesOnTop" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
  *                 &lt;/restriction>
  *               &lt;/complexContent>
  *             &lt;/complexType>
@@ -4808,6 +4810,8 @@ public class JalviewModel {
      *       &lt;attribute name="textColThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
      *       &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" />
      *       &lt;attribute name="complementId" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="showComplementFeatures" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *       &lt;attribute name="showComplementFeaturesOnTop" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
      *     &lt;/restriction>
      *   &lt;/complexContent>
      * &lt;/complexType>
@@ -4914,6 +4918,10 @@ public class JalviewModel {
         protected String id;
         @XmlAttribute(name = "complementId")
         protected String complementId;
+        @XmlAttribute(name = "showComplementFeatures")
+        protected Boolean showComplementFeatures;
+        @XmlAttribute(name = "showComplementFeaturesOnTop")
+        protected Boolean showComplementFeaturesOnTop;
         @XmlAttribute(name = "width")
         protected Integer width;
         @XmlAttribute(name = "height")
@@ -6034,6 +6042,62 @@ public class JalviewModel {
         }
 
         /**
+         * Gets the value of the showComplementFeatures property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowComplementFeatures() {
+            if (showComplementFeatures == null) {
+                return false;
+            } else {
+                return showComplementFeatures;
+            }
+        }
+
+        /**
+         * Sets the value of the showComplementFeatures property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowComplementFeatures(Boolean value) {
+            this.showComplementFeatures = value;
+        }
+
+        /**
+         * Gets the value of the showComplementFeaturesOnTop property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowComplementFeaturesOnTop() {
+            if (showComplementFeaturesOnTop == null) {
+                return false;
+            } else {
+                return showComplementFeaturesOnTop;
+            }
+        }
+
+        /**
+         * Sets the value of the showComplementFeaturesOnTop property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowComplementFeaturesOnTop(Boolean value) {
+            this.showComplementFeaturesOnTop = value;
+        }
+
+        /**
          * Gets the value of the width property.
          * 
          * @return
diff --git a/src/jalview/xml/binding/jalview/JalviewModelType.java b/src/jalview/xml/binding/jalview/JalviewModelType.java
new file mode 100644 (file)
index 0000000..f084bc3
--- /dev/null
@@ -0,0 +1,5090 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
+// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
+// Any modifications to this file will be lost upon recompilation of the source schema. 
+// Generated on: 2018.09.18 at 01:33:02 PM BST 
+//
+
+
+package jalview.xml.binding.jalview;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlID;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import javax.xml.datatype.XMLGregorianCalendar;
+
+
+/**
+ * <p>Java class for JalviewModelType complex type.
+ * 
+ * <p>The following schema fragment specifies the expected content contained within this class.
+ * 
+ * <pre>
+ * &lt;complexType name="JalviewModelType">
+ *   &lt;complexContent>
+ *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       &lt;sequence>
+ *         &lt;element name="creationDate" type="{http://www.w3.org/2001/XMLSchema}dateTime"/>
+ *         &lt;element name="version" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *         &lt;element name="vamsasModel" type="{www.vamsas.ac.uk/jalview/version2}VAMSAS"/>
+ *         &lt;sequence>
+ *           &lt;element name="JSeq" maxOccurs="unbounded" minOccurs="0">
+ *             &lt;complexType>
+ *               &lt;complexContent>
+ *                 &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                   &lt;sequence>
+ *                     &lt;element name="features" type="{www.jalview.org}feature" maxOccurs="unbounded" minOccurs="0"/>
+ *                     &lt;element name="pdbids" maxOccurs="unbounded" minOccurs="0">
+ *                       &lt;complexType>
+ *                         &lt;complexContent>
+ *                           &lt;extension base="{www.jalview.org}pdbentry">
+ *                             &lt;sequence>
+ *                               &lt;element name="structureState" maxOccurs="unbounded" minOccurs="0">
+ *                                 &lt;complexType>
+ *                                   &lt;simpleContent>
+ *                                     &lt;extension base="&lt;http://www.w3.org/2001/XMLSchema>string">
+ *                                       &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+ *                                       &lt;attribute name="visible" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                                       &lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                                       &lt;attribute name="alignwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+ *                                       &lt;attribute name="colourwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                                       &lt;attribute name="colourByJmol" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+ *                                       &lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                                     &lt;/extension>
+ *                                   &lt;/simpleContent>
+ *                                 &lt;/complexType>
+ *                               &lt;/element>
+ *                             &lt;/sequence>
+ *                           &lt;/extension>
+ *                         &lt;/complexContent>
+ *                       &lt;/complexType>
+ *                     &lt;/element>
+ *                     &lt;element name="hiddenSequences" type="{http://www.w3.org/2001/XMLSchema}int" maxOccurs="unbounded" minOccurs="0"/>
+ *                     &lt;element name="rnaViewer" maxOccurs="unbounded" minOccurs="0">
+ *                       &lt;complexType>
+ *                         &lt;complexContent>
+ *                           &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                             &lt;sequence>
+ *                               &lt;element name="secondaryStructure" maxOccurs="unbounded">
+ *                                 &lt;complexType>
+ *                                   &lt;complexContent>
+ *                                     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                                       &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                                       &lt;attribute name="annotationId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                                       &lt;attribute name="gapped" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                                       &lt;attribute name="viewerState" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                                     &lt;/restriction>
+ *                                   &lt;/complexContent>
+ *                                 &lt;/complexType>
+ *                               &lt;/element>
+ *                             &lt;/sequence>
+ *                             &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+ *                             &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                             &lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                             &lt;attribute name="dividerLocation" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                             &lt;attribute name="selectedRna" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                           &lt;/restriction>
+ *                         &lt;/complexContent>
+ *                       &lt;/complexType>
+ *                     &lt;/element>
+ *                   &lt;/sequence>
+ *                   &lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="start" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="end" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                   &lt;attribute name="hidden" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="viewreference" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                 &lt;/restriction>
+ *               &lt;/complexContent>
+ *             &lt;/complexType>
+ *           &lt;/element>
+ *           &lt;element name="JGroup" maxOccurs="unbounded" minOccurs="0">
+ *             &lt;complexType>
+ *               &lt;complexContent>
+ *                 &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                   &lt;sequence>
+ *                     &lt;element name="seq" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded"/>
+ *                     &lt;element name="annotationColours" type="{www.jalview.org}AnnotationColourScheme" minOccurs="0"/>
+ *                   &lt;/sequence>
+ *                   &lt;attribute name="start" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="end" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="name" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                   &lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                   &lt;attribute name="consThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="pidThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="outlineColour" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="displayBoxes" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="displayText" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="colourText" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="textCol1" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="textCol2" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="textColThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="showUnconserved" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="ignoreGapsinConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+ *                   &lt;attribute name="showConsensusHistogram" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+ *                   &lt;attribute name="showSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                   &lt;attribute name="normaliseSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                   &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                 &lt;/restriction>
+ *               &lt;/complexContent>
+ *             &lt;/complexType>
+ *           &lt;/element>
+ *           &lt;element name="Viewport" maxOccurs="unbounded" minOccurs="0">
+ *             &lt;complexType>
+ *               &lt;complexContent>
+ *                 &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                   &lt;sequence>
+ *                     &lt;element name="AnnotationColours" type="{www.jalview.org}AnnotationColourScheme" minOccurs="0"/>
+ *                     &lt;element name="hiddenColumns" maxOccurs="unbounded" minOccurs="0">
+ *                       &lt;complexType>
+ *                         &lt;complexContent>
+ *                           &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                             &lt;attribute name="start" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                             &lt;attribute name="end" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                           &lt;/restriction>
+ *                         &lt;/complexContent>
+ *                       &lt;/complexType>
+ *                     &lt;/element>
+ *                     &lt;element name="calcIdParam" maxOccurs="unbounded" minOccurs="0">
+ *                       &lt;complexType>
+ *                         &lt;complexContent>
+ *                           &lt;extension base="{www.jalview.org/xml/wsparamset}WebServiceParameterSet">
+ *                             &lt;attribute name="calcId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                             &lt;attribute name="needsUpdate" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                             &lt;attribute name="autoUpdate" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                           &lt;/extension>
+ *                         &lt;/complexContent>
+ *                       &lt;/complexType>
+ *                     &lt;/element>
+ *                   &lt;/sequence>
+ *                   &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+ *                   &lt;attribute name="conservationSelected" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="pidSelected" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="bgColour" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                   &lt;attribute name="consThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="pidThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                   &lt;attribute name="showFullId" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="rightAlignIds" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="showText" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="showColourText" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="showUnconserved" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                   &lt;attribute name="showBoxes" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="wrapAlignment" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="renderGaps" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="showSequenceFeatures" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="showNPfeatureTooltip" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="showDbRefTooltip" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="followHighlight" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+ *                   &lt;attribute name="followSelection" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+ *                   &lt;attribute name="showAnnotation" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="centreColumnLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                   &lt;attribute name="showGroupConservation" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                   &lt;attribute name="showGroupConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                   &lt;attribute name="showConsensusHistogram" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+ *                   &lt;attribute name="showSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                   &lt;attribute name="normaliseSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+ *                   &lt;attribute name="ignoreGapsinConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+ *                   &lt;attribute name="startRes" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="startSeq" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="fontName" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                   &lt;attribute name="fontSize" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="fontStyle" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="scaleProteinAsCdna" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+ *                   &lt;attribute name="viewName" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                   &lt;attribute name="sequenceSetId" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                   &lt;attribute name="gatheredViews" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="textCol1" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="textCol2" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="textColThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" />
+ *                   &lt;attribute name="complementId" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                 &lt;/restriction>
+ *               &lt;/complexContent>
+ *             &lt;/complexType>
+ *           &lt;/element>
+ *           &lt;element name="UserColours" maxOccurs="unbounded" minOccurs="0">
+ *             &lt;complexType>
+ *               &lt;complexContent>
+ *                 &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                   &lt;sequence>
+ *                     &lt;element name="UserColourScheme" type="{www.jalview.org/colours}JalviewUserColours"/>
+ *                   &lt;/sequence>
+ *                   &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                 &lt;/restriction>
+ *               &lt;/complexContent>
+ *             &lt;/complexType>
+ *           &lt;/element>
+ *           &lt;element name="tree" maxOccurs="unbounded" minOccurs="0">
+ *             &lt;complexType>
+ *               &lt;complexContent>
+ *                 &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                   &lt;sequence minOccurs="0">
+ *                     &lt;element name="title" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *                     &lt;element name="newick" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *                   &lt;/sequence>
+ *                   &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+ *                   &lt;attribute name="fontName" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                   &lt;attribute name="fontSize" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="fontStyle" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                   &lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                   &lt;attribute name="showBootstrap" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="showDistances" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="markUnlinked" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="fitToWindow" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="currentTree" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                   &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" />
+ *                 &lt;/restriction>
+ *               &lt;/complexContent>
+ *             &lt;/complexType>
+ *           &lt;/element>
+ *           &lt;element name="FeatureSettings" minOccurs="0">
+ *             &lt;complexType>
+ *               &lt;complexContent>
+ *                 &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                   &lt;sequence>
+ *                     &lt;element name="setting" maxOccurs="unbounded" minOccurs="0">
+ *                       &lt;complexType>
+ *                         &lt;complexContent>
+ *                           &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                             &lt;sequence>
+ *                               &lt;element name="attributeName" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="2" minOccurs="0"/>
+ *                               &lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet" minOccurs="0"/>
+ *                             &lt;/sequence>
+ *                             &lt;attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                             &lt;attribute name="colour" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                             &lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                             &lt;attribute name="order" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                             &lt;attribute name="mincolour" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                             &lt;attribute name="noValueColour" type="{www.jalview.org/colours}NoValueColour" default="Min" />
+ *                             &lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                             &lt;attribute name="threshstate" type="{http://www.w3.org/2001/XMLSchema}int" />
+ *                             &lt;attribute name="max" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                             &lt;attribute name="min" type="{http://www.w3.org/2001/XMLSchema}float" />
+ *                             &lt;attribute name="colourByLabel" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                             &lt;attribute name="autoScale" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                           &lt;/restriction>
+ *                         &lt;/complexContent>
+ *                       &lt;/complexType>
+ *                     &lt;/element>
+ *                     &lt;element name="group" maxOccurs="unbounded" minOccurs="0">
+ *                       &lt;complexType>
+ *                         &lt;complexContent>
+ *                           &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *                             &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                             &lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+ *                           &lt;/restriction>
+ *                         &lt;/complexContent>
+ *                       &lt;/complexType>
+ *                     &lt;/element>
+ *                   &lt;/sequence>
+ *                 &lt;/restriction>
+ *               &lt;/complexContent>
+ *             &lt;/complexType>
+ *           &lt;/element>
+ *         &lt;/sequence>
+ *       &lt;/sequence>
+ *     &lt;/restriction>
+ *   &lt;/complexContent>
+ * &lt;/complexType>
+ * </pre>
+ * 
+ * 
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "JalviewModelType", namespace = "www.jalview.org", propOrder = {
+    "creationDate",
+    "version",
+    "vamsasModel",
+    "jSeq",
+    "jGroup",
+    "viewport",
+    "userColours",
+    "tree",
+    "featureSettings"
+})
+public class JalviewModelType {
+
+    @XmlElement(required = true)
+    @XmlSchemaType(name = "dateTime")
+    protected XMLGregorianCalendar creationDate;
+    @XmlElement(required = true)
+    protected String version;
+    @XmlElement(required = true)
+    protected VAMSAS vamsasModel;
+    @XmlElement(name = "JSeq")
+    protected List<JalviewModelType.JSeq> jSeq;
+    @XmlElement(name = "JGroup")
+    protected List<JalviewModelType.JGroup> jGroup;
+    @XmlElement(name = "Viewport")
+    protected List<JalviewModelType.Viewport> viewport;
+    @XmlElement(name = "UserColours")
+    protected List<JalviewModelType.UserColours> userColours;
+    protected List<JalviewModelType.Tree> tree;
+    @XmlElement(name = "FeatureSettings")
+    protected JalviewModelType.FeatureSettings featureSettings;
+
+    /**
+     * Gets the value of the creationDate property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link XMLGregorianCalendar }
+     *     
+     */
+    public XMLGregorianCalendar getCreationDate() {
+        return creationDate;
+    }
+
+    /**
+     * Sets the value of the creationDate property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link XMLGregorianCalendar }
+     *     
+     */
+    public void setCreationDate(XMLGregorianCalendar value) {
+        this.creationDate = value;
+    }
+
+    /**
+     * Gets the value of the version property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getVersion() {
+        return version;
+    }
+
+    /**
+     * Sets the value of the version property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setVersion(String value) {
+        this.version = value;
+    }
+
+    /**
+     * Gets the value of the vamsasModel property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link VAMSAS }
+     *     
+     */
+    public VAMSAS getVamsasModel() {
+        return vamsasModel;
+    }
+
+    /**
+     * Sets the value of the vamsasModel property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link VAMSAS }
+     *     
+     */
+    public void setVamsasModel(VAMSAS value) {
+        this.vamsasModel = value;
+    }
+
+    /**
+     * Gets the value of the jSeq property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the jSeq property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getJSeq().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModelType.JSeq }
+     * 
+     * 
+     */
+    public List<JalviewModelType.JSeq> getJSeq() {
+        if (jSeq == null) {
+            jSeq = new ArrayList<JalviewModelType.JSeq>();
+        }
+        return this.jSeq;
+    }
+
+    /**
+     * Gets the value of the jGroup property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the jGroup property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getJGroup().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModelType.JGroup }
+     * 
+     * 
+     */
+    public List<JalviewModelType.JGroup> getJGroup() {
+        if (jGroup == null) {
+            jGroup = new ArrayList<JalviewModelType.JGroup>();
+        }
+        return this.jGroup;
+    }
+
+    /**
+     * Gets the value of the viewport property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the viewport property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getViewport().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModelType.Viewport }
+     * 
+     * 
+     */
+    public List<JalviewModelType.Viewport> getViewport() {
+        if (viewport == null) {
+            viewport = new ArrayList<JalviewModelType.Viewport>();
+        }
+        return this.viewport;
+    }
+
+    /**
+     * Gets the value of the userColours property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the userColours property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getUserColours().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModelType.UserColours }
+     * 
+     * 
+     */
+    public List<JalviewModelType.UserColours> getUserColours() {
+        if (userColours == null) {
+            userColours = new ArrayList<JalviewModelType.UserColours>();
+        }
+        return this.userColours;
+    }
+
+    /**
+     * Gets the value of the tree property.
+     * 
+     * <p>
+     * This accessor method returns a reference to the live list,
+     * not a snapshot. Therefore any modification you make to the
+     * returned list will be present inside the JAXB object.
+     * This is why there is not a <CODE>set</CODE> method for the tree property.
+     * 
+     * <p>
+     * For example, to add a new item, do as follows:
+     * <pre>
+     *    getTree().add(newItem);
+     * </pre>
+     * 
+     * 
+     * <p>
+     * Objects of the following type(s) are allowed in the list
+     * {@link JalviewModelType.Tree }
+     * 
+     * 
+     */
+    public List<JalviewModelType.Tree> getTree() {
+        if (tree == null) {
+            tree = new ArrayList<JalviewModelType.Tree>();
+        }
+        return this.tree;
+    }
+
+    /**
+     * Gets the value of the featureSettings property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link JalviewModelType.FeatureSettings }
+     *     
+     */
+    public JalviewModelType.FeatureSettings getFeatureSettings() {
+        return featureSettings;
+    }
+
+    /**
+     * Sets the value of the featureSettings property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link JalviewModelType.FeatureSettings }
+     *     
+     */
+    public void setFeatureSettings(JalviewModelType.FeatureSettings value) {
+        this.featureSettings = value;
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * &lt;complexType>
+     *   &lt;complexContent>
+     *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       &lt;sequence>
+     *         &lt;element name="setting" maxOccurs="unbounded" minOccurs="0">
+     *           &lt;complexType>
+     *             &lt;complexContent>
+     *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 &lt;sequence>
+     *                   &lt;element name="attributeName" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="2" minOccurs="0"/>
+     *                   &lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet" minOccurs="0"/>
+     *                 &lt;/sequence>
+     *                 &lt;attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *                 &lt;attribute name="colour" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *                 &lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *                 &lt;attribute name="order" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                 &lt;attribute name="mincolour" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *                 &lt;attribute name="noValueColour" type="{www.jalview.org/colours}NoValueColour" default="Min" />
+     *                 &lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                 &lt;attribute name="threshstate" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *                 &lt;attribute name="max" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                 &lt;attribute name="min" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *                 &lt;attribute name="colourByLabel" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *                 &lt;attribute name="autoScale" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *               &lt;/restriction>
+     *             &lt;/complexContent>
+     *           &lt;/complexType>
+     *         &lt;/element>
+     *         &lt;element name="group" maxOccurs="unbounded" minOccurs="0">
+     *           &lt;complexType>
+     *             &lt;complexContent>
+     *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *                 &lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *               &lt;/restriction>
+     *             &lt;/complexContent>
+     *           &lt;/complexType>
+     *         &lt;/element>
+     *       &lt;/sequence>
+     *     &lt;/restriction>
+     *   &lt;/complexContent>
+     * &lt;/complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "setting",
+        "group"
+    })
+    public static class FeatureSettings {
+
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModelType.FeatureSettings.Setting> setting;
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModelType.FeatureSettings.Group> group;
+
+        /**
+         * Gets the value of the setting property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the setting property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getSetting().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModelType.FeatureSettings.Setting }
+         * 
+         * 
+         */
+        public List<JalviewModelType.FeatureSettings.Setting> getSetting() {
+            if (setting == null) {
+                setting = new ArrayList<JalviewModelType.FeatureSettings.Setting>();
+            }
+            return this.setting;
+        }
+
+        /**
+         * Gets the value of the group property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the group property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getGroup().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModelType.FeatureSettings.Group }
+         * 
+         * 
+         */
+        public List<JalviewModelType.FeatureSettings.Group> getGroup() {
+            if (group == null) {
+                group = new ArrayList<JalviewModelType.FeatureSettings.Group>();
+            }
+            return this.group;
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * &lt;complexType>
+         *   &lt;complexContent>
+         *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *       &lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+         *     &lt;/restriction>
+         *   &lt;/complexContent>
+         * &lt;/complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class Group {
+
+            @XmlAttribute(name = "name", required = true)
+            protected String name;
+            @XmlAttribute(name = "display", required = true)
+            protected boolean display;
+
+            /**
+             * Gets the value of the name property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getName() {
+                return name;
+            }
+
+            /**
+             * Sets the value of the name property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setName(String value) {
+                this.name = value;
+            }
+
+            /**
+             * Gets the value of the display property.
+             * 
+             */
+            public boolean isDisplay() {
+                return display;
+            }
+
+            /**
+             * Sets the value of the display property.
+             * 
+             */
+            public void setDisplay(boolean value) {
+                this.display = value;
+            }
+
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * &lt;complexType>
+         *   &lt;complexContent>
+         *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       &lt;sequence>
+         *         &lt;element name="attributeName" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="2" minOccurs="0"/>
+         *         &lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet" minOccurs="0"/>
+         *       &lt;/sequence>
+         *       &lt;attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *       &lt;attribute name="colour" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
+         *       &lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+         *       &lt;attribute name="order" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *       &lt;attribute name="mincolour" type="{http://www.w3.org/2001/XMLSchema}int" />
+         *       &lt;attribute name="noValueColour" type="{www.jalview.org/colours}NoValueColour" default="Min" />
+         *       &lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *       &lt;attribute name="threshstate" type="{http://www.w3.org/2001/XMLSchema}int" />
+         *       &lt;attribute name="max" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *       &lt;attribute name="min" type="{http://www.w3.org/2001/XMLSchema}float" />
+         *       &lt;attribute name="colourByLabel" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+         *       &lt;attribute name="autoScale" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+         *     &lt;/restriction>
+         *   &lt;/complexContent>
+         * &lt;/complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "attributeName",
+            "matcherSet"
+        })
+        public static class Setting {
+
+            @XmlElement(namespace = "www.jalview.org")
+            protected List<String> attributeName;
+            @XmlElement(namespace = "www.jalview.org")
+            protected FeatureMatcherSet matcherSet;
+            @XmlAttribute(name = "type", required = true)
+            protected String type;
+            @XmlAttribute(name = "colour", required = true)
+            protected int colour;
+            @XmlAttribute(name = "display", required = true)
+            protected boolean display;
+            @XmlAttribute(name = "order")
+            protected Float order;
+            @XmlAttribute(name = "mincolour")
+            protected Integer mincolour;
+            @XmlAttribute(name = "noValueColour")
+            protected NoValueColour noValueColour;
+            @XmlAttribute(name = "threshold")
+            protected Float threshold;
+            @XmlAttribute(name = "threshstate")
+            protected Integer threshstate;
+            @XmlAttribute(name = "max")
+            protected Float max;
+            @XmlAttribute(name = "min")
+            protected Float min;
+            @XmlAttribute(name = "colourByLabel")
+            protected Boolean colourByLabel;
+            @XmlAttribute(name = "autoScale")
+            protected Boolean autoScale;
+
+            /**
+             * Gets the value of the attributeName property.
+             * 
+             * <p>
+             * This accessor method returns a reference to the live list,
+             * not a snapshot. Therefore any modification you make to the
+             * returned list will be present inside the JAXB object.
+             * This is why there is not a <CODE>set</CODE> method for the attributeName property.
+             * 
+             * <p>
+             * For example, to add a new item, do as follows:
+             * <pre>
+             *    getAttributeName().add(newItem);
+             * </pre>
+             * 
+             * 
+             * <p>
+             * Objects of the following type(s) are allowed in the list
+             * {@link String }
+             * 
+             * 
+             */
+            public List<String> getAttributeName() {
+                if (attributeName == null) {
+                    attributeName = new ArrayList<String>();
+                }
+                return this.attributeName;
+            }
+
+            /**
+             * Gets the value of the matcherSet property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link FeatureMatcherSet }
+             *     
+             */
+            public FeatureMatcherSet getMatcherSet() {
+                return matcherSet;
+            }
+
+            /**
+             * Sets the value of the matcherSet property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link FeatureMatcherSet }
+             *     
+             */
+            public void setMatcherSet(FeatureMatcherSet value) {
+                this.matcherSet = value;
+            }
+
+            /**
+             * Gets the value of the type property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getType() {
+                return type;
+            }
+
+            /**
+             * Sets the value of the type property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setType(String value) {
+                this.type = value;
+            }
+
+            /**
+             * Gets the value of the colour property.
+             * 
+             */
+            public int getColour() {
+                return colour;
+            }
+
+            /**
+             * Sets the value of the colour property.
+             * 
+             */
+            public void setColour(int value) {
+                this.colour = value;
+            }
+
+            /**
+             * Gets the value of the display property.
+             * 
+             */
+            public boolean isDisplay() {
+                return display;
+            }
+
+            /**
+             * Sets the value of the display property.
+             * 
+             */
+            public void setDisplay(boolean value) {
+                this.display = value;
+            }
+
+            /**
+             * Gets the value of the order property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getOrder() {
+                return order;
+            }
+
+            /**
+             * Sets the value of the order property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setOrder(Float value) {
+                this.order = value;
+            }
+
+            /**
+             * Gets the value of the mincolour property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getMincolour() {
+                return mincolour;
+            }
+
+            /**
+             * Sets the value of the mincolour property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setMincolour(Integer value) {
+                this.mincolour = value;
+            }
+
+            /**
+             * Gets the value of the noValueColour property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link NoValueColour }
+             *     
+             */
+            public NoValueColour getNoValueColour() {
+                if (noValueColour == null) {
+                    return NoValueColour.MIN;
+                } else {
+                    return noValueColour;
+                }
+            }
+
+            /**
+             * Sets the value of the noValueColour property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link NoValueColour }
+             *     
+             */
+            public void setNoValueColour(NoValueColour value) {
+                this.noValueColour = value;
+            }
+
+            /**
+             * Gets the value of the threshold property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getThreshold() {
+                return threshold;
+            }
+
+            /**
+             * Sets the value of the threshold property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setThreshold(Float value) {
+                this.threshold = value;
+            }
+
+            /**
+             * Gets the value of the threshstate property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getThreshstate() {
+                return threshstate;
+            }
+
+            /**
+             * Sets the value of the threshstate property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setThreshstate(Integer value) {
+                this.threshstate = value;
+            }
+
+            /**
+             * Gets the value of the max property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getMax() {
+                return max;
+            }
+
+            /**
+             * Sets the value of the max property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setMax(Float value) {
+                this.max = value;
+            }
+
+            /**
+             * Gets the value of the min property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Float }
+             *     
+             */
+            public Float getMin() {
+                return min;
+            }
+
+            /**
+             * Sets the value of the min property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Float }
+             *     
+             */
+            public void setMin(Float value) {
+                this.min = value;
+            }
+
+            /**
+             * Gets the value of the colourByLabel property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Boolean }
+             *     
+             */
+            public Boolean isColourByLabel() {
+                return colourByLabel;
+            }
+
+            /**
+             * Sets the value of the colourByLabel property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Boolean }
+             *     
+             */
+            public void setColourByLabel(Boolean value) {
+                this.colourByLabel = value;
+            }
+
+            /**
+             * Gets the value of the autoScale property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Boolean }
+             *     
+             */
+            public Boolean isAutoScale() {
+                return autoScale;
+            }
+
+            /**
+             * Sets the value of the autoScale property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Boolean }
+             *     
+             */
+            public void setAutoScale(Boolean value) {
+                this.autoScale = value;
+            }
+
+        }
+
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * &lt;complexType>
+     *   &lt;complexContent>
+     *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       &lt;sequence>
+     *         &lt;element name="seq" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded"/>
+     *         &lt;element name="annotationColours" type="{www.jalview.org}AnnotationColourScheme" minOccurs="0"/>
+     *       &lt;/sequence>
+     *       &lt;attribute name="start" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="end" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="name" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="consThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="pidThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="outlineColour" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="displayBoxes" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="displayText" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="colourText" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="textCol1" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="textCol2" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="textColThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="showUnconserved" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="ignoreGapsinConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+     *       &lt;attribute name="showConsensusHistogram" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+     *       &lt;attribute name="showSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *       &lt;attribute name="normaliseSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *       &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *     &lt;/restriction>
+     *   &lt;/complexContent>
+     * &lt;/complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "seq",
+        "annotationColours"
+    })
+    public static class JGroup {
+
+        @XmlElement(namespace = "www.jalview.org", required = true)
+        protected List<String> seq;
+        @XmlElement(namespace = "www.jalview.org")
+        protected AnnotationColourScheme annotationColours;
+        @XmlAttribute(name = "start")
+        protected Integer start;
+        @XmlAttribute(name = "end")
+        protected Integer end;
+        @XmlAttribute(name = "name")
+        protected String name;
+        @XmlAttribute(name = "colour")
+        protected String colour;
+        @XmlAttribute(name = "consThreshold")
+        protected Integer consThreshold;
+        @XmlAttribute(name = "pidThreshold")
+        protected Integer pidThreshold;
+        @XmlAttribute(name = "outlineColour")
+        protected Integer outlineColour;
+        @XmlAttribute(name = "displayBoxes")
+        protected Boolean displayBoxes;
+        @XmlAttribute(name = "displayText")
+        protected Boolean displayText;
+        @XmlAttribute(name = "colourText")
+        protected Boolean colourText;
+        @XmlAttribute(name = "textCol1")
+        protected Integer textCol1;
+        @XmlAttribute(name = "textCol2")
+        protected Integer textCol2;
+        @XmlAttribute(name = "textColThreshold")
+        protected Integer textColThreshold;
+        @XmlAttribute(name = "showUnconserved")
+        protected Boolean showUnconserved;
+        @XmlAttribute(name = "ignoreGapsinConsensus")
+        protected Boolean ignoreGapsinConsensus;
+        @XmlAttribute(name = "showConsensusHistogram")
+        protected Boolean showConsensusHistogram;
+        @XmlAttribute(name = "showSequenceLogo")
+        protected Boolean showSequenceLogo;
+        @XmlAttribute(name = "normaliseSequenceLogo")
+        protected Boolean normaliseSequenceLogo;
+        @XmlAttribute(name = "id")
+        protected String id;
+
+        /**
+         * Gets the value of the seq property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the seq property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getSeq().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link String }
+         * 
+         * 
+         */
+        public List<String> getSeq() {
+            if (seq == null) {
+                seq = new ArrayList<String>();
+            }
+            return this.seq;
+        }
+
+        /**
+         * Gets the value of the annotationColours property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link AnnotationColourScheme }
+         *     
+         */
+        public AnnotationColourScheme getAnnotationColours() {
+            return annotationColours;
+        }
+
+        /**
+         * Sets the value of the annotationColours property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link AnnotationColourScheme }
+         *     
+         */
+        public void setAnnotationColours(AnnotationColourScheme value) {
+            this.annotationColours = value;
+        }
+
+        /**
+         * Gets the value of the start property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getStart() {
+            return start;
+        }
+
+        /**
+         * Sets the value of the start property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setStart(Integer value) {
+            this.start = value;
+        }
+
+        /**
+         * Gets the value of the end property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getEnd() {
+            return end;
+        }
+
+        /**
+         * Sets the value of the end property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setEnd(Integer value) {
+            this.end = value;
+        }
+
+        /**
+         * Gets the value of the name property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * Sets the value of the name property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setName(String value) {
+            this.name = value;
+        }
+
+        /**
+         * Gets the value of the colour property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getColour() {
+            return colour;
+        }
+
+        /**
+         * Sets the value of the colour property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setColour(String value) {
+            this.colour = value;
+        }
+
+        /**
+         * Gets the value of the consThreshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getConsThreshold() {
+            return consThreshold;
+        }
+
+        /**
+         * Sets the value of the consThreshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setConsThreshold(Integer value) {
+            this.consThreshold = value;
+        }
+
+        /**
+         * Gets the value of the pidThreshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getPidThreshold() {
+            return pidThreshold;
+        }
+
+        /**
+         * Sets the value of the pidThreshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setPidThreshold(Integer value) {
+            this.pidThreshold = value;
+        }
+
+        /**
+         * Gets the value of the outlineColour property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getOutlineColour() {
+            return outlineColour;
+        }
+
+        /**
+         * Sets the value of the outlineColour property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setOutlineColour(Integer value) {
+            this.outlineColour = value;
+        }
+
+        /**
+         * Gets the value of the displayBoxes property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isDisplayBoxes() {
+            return displayBoxes;
+        }
+
+        /**
+         * Sets the value of the displayBoxes property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setDisplayBoxes(Boolean value) {
+            this.displayBoxes = value;
+        }
+
+        /**
+         * Gets the value of the displayText property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isDisplayText() {
+            return displayText;
+        }
+
+        /**
+         * Sets the value of the displayText property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setDisplayText(Boolean value) {
+            this.displayText = value;
+        }
+
+        /**
+         * Gets the value of the colourText property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isColourText() {
+            return colourText;
+        }
+
+        /**
+         * Sets the value of the colourText property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setColourText(Boolean value) {
+            this.colourText = value;
+        }
+
+        /**
+         * Gets the value of the textCol1 property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextCol1() {
+            return textCol1;
+        }
+
+        /**
+         * Sets the value of the textCol1 property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextCol1(Integer value) {
+            this.textCol1 = value;
+        }
+
+        /**
+         * Gets the value of the textCol2 property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextCol2() {
+            return textCol2;
+        }
+
+        /**
+         * Sets the value of the textCol2 property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextCol2(Integer value) {
+            this.textCol2 = value;
+        }
+
+        /**
+         * Gets the value of the textColThreshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextColThreshold() {
+            return textColThreshold;
+        }
+
+        /**
+         * Sets the value of the textColThreshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextColThreshold(Integer value) {
+            this.textColThreshold = value;
+        }
+
+        /**
+         * Gets the value of the showUnconserved property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowUnconserved() {
+            return showUnconserved;
+        }
+
+        /**
+         * Sets the value of the showUnconserved property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowUnconserved(Boolean value) {
+            this.showUnconserved = value;
+        }
+
+        /**
+         * Gets the value of the ignoreGapsinConsensus property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isIgnoreGapsinConsensus() {
+            if (ignoreGapsinConsensus == null) {
+                return true;
+            } else {
+                return ignoreGapsinConsensus;
+            }
+        }
+
+        /**
+         * Sets the value of the ignoreGapsinConsensus property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setIgnoreGapsinConsensus(Boolean value) {
+            this.ignoreGapsinConsensus = value;
+        }
+
+        /**
+         * Gets the value of the showConsensusHistogram property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowConsensusHistogram() {
+            if (showConsensusHistogram == null) {
+                return true;
+            } else {
+                return showConsensusHistogram;
+            }
+        }
+
+        /**
+         * Sets the value of the showConsensusHistogram property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowConsensusHistogram(Boolean value) {
+            this.showConsensusHistogram = value;
+        }
+
+        /**
+         * Gets the value of the showSequenceLogo property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowSequenceLogo() {
+            if (showSequenceLogo == null) {
+                return false;
+            } else {
+                return showSequenceLogo;
+            }
+        }
+
+        /**
+         * Sets the value of the showSequenceLogo property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowSequenceLogo(Boolean value) {
+            this.showSequenceLogo = value;
+        }
+
+        /**
+         * Gets the value of the normaliseSequenceLogo property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isNormaliseSequenceLogo() {
+            if (normaliseSequenceLogo == null) {
+                return false;
+            } else {
+                return normaliseSequenceLogo;
+            }
+        }
+
+        /**
+         * Sets the value of the normaliseSequenceLogo property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setNormaliseSequenceLogo(Boolean value) {
+            this.normaliseSequenceLogo = value;
+        }
+
+        /**
+         * Gets the value of the id property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getId() {
+            return id;
+        }
+
+        /**
+         * Sets the value of the id property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setId(String value) {
+            this.id = value;
+        }
+
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * &lt;complexType>
+     *   &lt;complexContent>
+     *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       &lt;sequence>
+     *         &lt;element name="features" type="{www.jalview.org}feature" maxOccurs="unbounded" minOccurs="0"/>
+     *         &lt;element name="pdbids" maxOccurs="unbounded" minOccurs="0">
+     *           &lt;complexType>
+     *             &lt;complexContent>
+     *               &lt;extension base="{www.jalview.org}pdbentry">
+     *                 &lt;sequence>
+     *                   &lt;element name="structureState" maxOccurs="unbounded" minOccurs="0">
+     *                     &lt;complexType>
+     *                       &lt;simpleContent>
+     *                         &lt;extension base="&lt;http://www.w3.org/2001/XMLSchema>string">
+     *                           &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+     *                           &lt;attribute name="visible" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *                           &lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *                           &lt;attribute name="alignwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+     *                           &lt;attribute name="colourwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *                           &lt;attribute name="colourByJmol" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+     *                           &lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *                         &lt;/extension>
+     *                       &lt;/simpleContent>
+     *                     &lt;/complexType>
+     *                   &lt;/element>
+     *                 &lt;/sequence>
+     *               &lt;/extension>
+     *             &lt;/complexContent>
+     *           &lt;/complexType>
+     *         &lt;/element>
+     *         &lt;element name="hiddenSequences" type="{http://www.w3.org/2001/XMLSchema}int" maxOccurs="unbounded" minOccurs="0"/>
+     *         &lt;element name="rnaViewer" maxOccurs="unbounded" minOccurs="0">
+     *           &lt;complexType>
+     *             &lt;complexContent>
+     *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 &lt;sequence>
+     *                   &lt;element name="secondaryStructure" maxOccurs="unbounded">
+     *                     &lt;complexType>
+     *                       &lt;complexContent>
+     *                         &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                           &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *                           &lt;attribute name="annotationId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *                           &lt;attribute name="gapped" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *                           &lt;attribute name="viewerState" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *                         &lt;/restriction>
+     *                       &lt;/complexContent>
+     *                     &lt;/complexType>
+     *                   &lt;/element>
+     *                 &lt;/sequence>
+     *                 &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+     *                 &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *                 &lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *                 &lt;attribute name="dividerLocation" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *                 &lt;attribute name="selectedRna" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *               &lt;/restriction>
+     *             &lt;/complexContent>
+     *           &lt;/complexType>
+     *         &lt;/element>
+     *       &lt;/sequence>
+     *       &lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="start" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="end" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="hidden" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="viewreference" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *     &lt;/restriction>
+     *   &lt;/complexContent>
+     * &lt;/complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "features",
+        "pdbids",
+        "hiddenSequences",
+        "rnaViewer"
+    })
+    public static class JSeq {
+
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<Feature> features;
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModelType.JSeq.Pdbids> pdbids;
+        @XmlElement(namespace = "www.jalview.org", type = Integer.class)
+        protected List<Integer> hiddenSequences;
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModelType.JSeq.RnaViewer> rnaViewer;
+        @XmlAttribute(name = "colour")
+        protected Integer colour;
+        @XmlAttribute(name = "start", required = true)
+        protected int start;
+        @XmlAttribute(name = "end", required = true)
+        protected int end;
+        @XmlAttribute(name = "id", required = true)
+        protected String id;
+        @XmlAttribute(name = "hidden")
+        protected Boolean hidden;
+        @XmlAttribute(name = "viewreference")
+        protected Boolean viewreference;
+
+        /**
+         * Gets the value of the features property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the features property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getFeatures().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link Feature }
+         * 
+         * 
+         */
+        public List<Feature> getFeatures() {
+            if (features == null) {
+                features = new ArrayList<Feature>();
+            }
+            return this.features;
+        }
+
+        /**
+         * Gets the value of the pdbids property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the pdbids property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getPdbids().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModelType.JSeq.Pdbids }
+         * 
+         * 
+         */
+        public List<JalviewModelType.JSeq.Pdbids> getPdbids() {
+            if (pdbids == null) {
+                pdbids = new ArrayList<JalviewModelType.JSeq.Pdbids>();
+            }
+            return this.pdbids;
+        }
+
+        /**
+         * Gets the value of the hiddenSequences property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the hiddenSequences property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getHiddenSequences().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link Integer }
+         * 
+         * 
+         */
+        public List<Integer> getHiddenSequences() {
+            if (hiddenSequences == null) {
+                hiddenSequences = new ArrayList<Integer>();
+            }
+            return this.hiddenSequences;
+        }
+
+        /**
+         * Gets the value of the rnaViewer property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the rnaViewer property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getRnaViewer().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModelType.JSeq.RnaViewer }
+         * 
+         * 
+         */
+        public List<JalviewModelType.JSeq.RnaViewer> getRnaViewer() {
+            if (rnaViewer == null) {
+                rnaViewer = new ArrayList<JalviewModelType.JSeq.RnaViewer>();
+            }
+            return this.rnaViewer;
+        }
+
+        /**
+         * Gets the value of the colour property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getColour() {
+            return colour;
+        }
+
+        /**
+         * Sets the value of the colour property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setColour(Integer value) {
+            this.colour = value;
+        }
+
+        /**
+         * Gets the value of the start property.
+         * 
+         */
+        public int getStart() {
+            return start;
+        }
+
+        /**
+         * Sets the value of the start property.
+         * 
+         */
+        public void setStart(int value) {
+            this.start = value;
+        }
+
+        /**
+         * Gets the value of the end property.
+         * 
+         */
+        public int getEnd() {
+            return end;
+        }
+
+        /**
+         * Sets the value of the end property.
+         * 
+         */
+        public void setEnd(int value) {
+            this.end = value;
+        }
+
+        /**
+         * Gets the value of the id property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getId() {
+            return id;
+        }
+
+        /**
+         * Sets the value of the id property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setId(String value) {
+            this.id = value;
+        }
+
+        /**
+         * Gets the value of the hidden property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isHidden() {
+            return hidden;
+        }
+
+        /**
+         * Sets the value of the hidden property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setHidden(Boolean value) {
+            this.hidden = value;
+        }
+
+        /**
+         * Gets the value of the viewreference property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isViewreference() {
+            return viewreference;
+        }
+
+        /**
+         * Sets the value of the viewreference property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setViewreference(Boolean value) {
+            this.viewreference = value;
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * &lt;complexType>
+         *   &lt;complexContent>
+         *     &lt;extension base="{www.jalview.org}pdbentry">
+         *       &lt;sequence>
+         *         &lt;element name="structureState" maxOccurs="unbounded" minOccurs="0">
+         *           &lt;complexType>
+         *             &lt;simpleContent>
+         *               &lt;extension base="&lt;http://www.w3.org/2001/XMLSchema>string">
+         *                 &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+         *                 &lt;attribute name="visible" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+         *                 &lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *                 &lt;attribute name="alignwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+         *                 &lt;attribute name="colourwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+         *                 &lt;attribute name="colourByJmol" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+         *                 &lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *               &lt;/extension>
+         *             &lt;/simpleContent>
+         *           &lt;/complexType>
+         *         &lt;/element>
+         *       &lt;/sequence>
+         *     &lt;/extension>
+         *   &lt;/complexContent>
+         * &lt;/complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "structureState"
+        })
+        public static class Pdbids
+            extends Pdbentry
+        {
+
+            @XmlElement(namespace = "www.jalview.org")
+            protected List<JalviewModelType.JSeq.Pdbids.StructureState> structureState;
+
+            /**
+             * Gets the value of the structureState property.
+             * 
+             * <p>
+             * This accessor method returns a reference to the live list,
+             * not a snapshot. Therefore any modification you make to the
+             * returned list will be present inside the JAXB object.
+             * This is why there is not a <CODE>set</CODE> method for the structureState property.
+             * 
+             * <p>
+             * For example, to add a new item, do as follows:
+             * <pre>
+             *    getStructureState().add(newItem);
+             * </pre>
+             * 
+             * 
+             * <p>
+             * Objects of the following type(s) are allowed in the list
+             * {@link JalviewModelType.JSeq.Pdbids.StructureState }
+             * 
+             * 
+             */
+            public List<JalviewModelType.JSeq.Pdbids.StructureState> getStructureState() {
+                if (structureState == null) {
+                    structureState = new ArrayList<JalviewModelType.JSeq.Pdbids.StructureState>();
+                }
+                return this.structureState;
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * &lt;complexType>
+             *   &lt;simpleContent>
+             *     &lt;extension base="&lt;http://www.w3.org/2001/XMLSchema>string">
+             *       &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+             *       &lt;attribute name="visible" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+             *       &lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" />
+             *       &lt;attribute name="alignwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+             *       &lt;attribute name="colourwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+             *       &lt;attribute name="colourByJmol" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+             *       &lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" />
+             *     &lt;/extension>
+             *   &lt;/simpleContent>
+             * &lt;/complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "", propOrder = {
+                "value"
+            })
+            public static class StructureState {
+
+                @XmlValue
+                protected String value;
+                @XmlAttribute(name = "visible")
+                protected Boolean visible;
+                @XmlAttribute(name = "viewId")
+                protected String viewId;
+                @XmlAttribute(name = "alignwithAlignPanel")
+                protected Boolean alignwithAlignPanel;
+                @XmlAttribute(name = "colourwithAlignPanel")
+                protected Boolean colourwithAlignPanel;
+                @XmlAttribute(name = "colourByJmol")
+                protected Boolean colourByJmol;
+                @XmlAttribute(name = "type")
+                protected String type;
+                @XmlAttribute(name = "width")
+                protected Integer width;
+                @XmlAttribute(name = "height")
+                protected Integer height;
+                @XmlAttribute(name = "xpos")
+                protected Integer xpos;
+                @XmlAttribute(name = "ypos")
+                protected Integer ypos;
+
+                /**
+                 * Gets the value of the value property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getValue() {
+                    return value;
+                }
+
+                /**
+                 * Sets the value of the value property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setValue(String value) {
+                    this.value = value;
+                }
+
+                /**
+                 * Gets the value of the visible property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public Boolean isVisible() {
+                    return visible;
+                }
+
+                /**
+                 * Sets the value of the visible property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public void setVisible(Boolean value) {
+                    this.visible = value;
+                }
+
+                /**
+                 * Gets the value of the viewId property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getViewId() {
+                    return viewId;
+                }
+
+                /**
+                 * Sets the value of the viewId property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setViewId(String value) {
+                    this.viewId = value;
+                }
+
+                /**
+                 * Gets the value of the alignwithAlignPanel property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public boolean isAlignwithAlignPanel() {
+                    if (alignwithAlignPanel == null) {
+                        return true;
+                    } else {
+                        return alignwithAlignPanel;
+                    }
+                }
+
+                /**
+                 * Sets the value of the alignwithAlignPanel property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public void setAlignwithAlignPanel(Boolean value) {
+                    this.alignwithAlignPanel = value;
+                }
+
+                /**
+                 * Gets the value of the colourwithAlignPanel property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public boolean isColourwithAlignPanel() {
+                    if (colourwithAlignPanel == null) {
+                        return false;
+                    } else {
+                        return colourwithAlignPanel;
+                    }
+                }
+
+                /**
+                 * Sets the value of the colourwithAlignPanel property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public void setColourwithAlignPanel(Boolean value) {
+                    this.colourwithAlignPanel = value;
+                }
+
+                /**
+                 * Gets the value of the colourByJmol property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public boolean isColourByJmol() {
+                    if (colourByJmol == null) {
+                        return true;
+                    } else {
+                        return colourByJmol;
+                    }
+                }
+
+                /**
+                 * Sets the value of the colourByJmol property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public void setColourByJmol(Boolean value) {
+                    this.colourByJmol = value;
+                }
+
+                /**
+                 * Gets the value of the type property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getType() {
+                    return type;
+                }
+
+                /**
+                 * Sets the value of the type property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setType(String value) {
+                    this.type = value;
+                }
+
+                /**
+                 * Gets the value of the width property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public Integer getWidth() {
+                    return width;
+                }
+
+                /**
+                 * Sets the value of the width property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public void setWidth(Integer value) {
+                    this.width = value;
+                }
+
+                /**
+                 * Gets the value of the height property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public Integer getHeight() {
+                    return height;
+                }
+
+                /**
+                 * Sets the value of the height property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public void setHeight(Integer value) {
+                    this.height = value;
+                }
+
+                /**
+                 * Gets the value of the xpos property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public Integer getXpos() {
+                    return xpos;
+                }
+
+                /**
+                 * Sets the value of the xpos property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public void setXpos(Integer value) {
+                    this.xpos = value;
+                }
+
+                /**
+                 * Gets the value of the ypos property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public Integer getYpos() {
+                    return ypos;
+                }
+
+                /**
+                 * Sets the value of the ypos property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Integer }
+                 *     
+                 */
+                public void setYpos(Integer value) {
+                    this.ypos = value;
+                }
+
+            }
+
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * &lt;complexType>
+         *   &lt;complexContent>
+         *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       &lt;sequence>
+         *         &lt;element name="secondaryStructure" maxOccurs="unbounded">
+         *           &lt;complexType>
+         *             &lt;complexContent>
+         *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *                 &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *                 &lt;attribute name="annotationId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *                 &lt;attribute name="gapped" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+         *                 &lt;attribute name="viewerState" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *               &lt;/restriction>
+         *             &lt;/complexContent>
+         *           &lt;/complexType>
+         *         &lt;/element>
+         *       &lt;/sequence>
+         *       &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+         *       &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *       &lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *       &lt;attribute name="dividerLocation" type="{http://www.w3.org/2001/XMLSchema}int" />
+         *       &lt;attribute name="selectedRna" type="{http://www.w3.org/2001/XMLSchema}int" />
+         *     &lt;/restriction>
+         *   &lt;/complexContent>
+         * &lt;/complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "", propOrder = {
+            "secondaryStructure"
+        })
+        public static class RnaViewer {
+
+            @XmlElement(namespace = "www.jalview.org", required = true)
+            protected List<JalviewModelType.JSeq.RnaViewer.SecondaryStructure> secondaryStructure;
+            @XmlAttribute(name = "title")
+            protected String title;
+            @XmlAttribute(name = "viewId")
+            protected String viewId;
+            @XmlAttribute(name = "dividerLocation")
+            protected Integer dividerLocation;
+            @XmlAttribute(name = "selectedRna")
+            protected Integer selectedRna;
+            @XmlAttribute(name = "width")
+            protected Integer width;
+            @XmlAttribute(name = "height")
+            protected Integer height;
+            @XmlAttribute(name = "xpos")
+            protected Integer xpos;
+            @XmlAttribute(name = "ypos")
+            protected Integer ypos;
+
+            /**
+             * Gets the value of the secondaryStructure property.
+             * 
+             * <p>
+             * This accessor method returns a reference to the live list,
+             * not a snapshot. Therefore any modification you make to the
+             * returned list will be present inside the JAXB object.
+             * This is why there is not a <CODE>set</CODE> method for the secondaryStructure property.
+             * 
+             * <p>
+             * For example, to add a new item, do as follows:
+             * <pre>
+             *    getSecondaryStructure().add(newItem);
+             * </pre>
+             * 
+             * 
+             * <p>
+             * Objects of the following type(s) are allowed in the list
+             * {@link JalviewModelType.JSeq.RnaViewer.SecondaryStructure }
+             * 
+             * 
+             */
+            public List<JalviewModelType.JSeq.RnaViewer.SecondaryStructure> getSecondaryStructure() {
+                if (secondaryStructure == null) {
+                    secondaryStructure = new ArrayList<JalviewModelType.JSeq.RnaViewer.SecondaryStructure>();
+                }
+                return this.secondaryStructure;
+            }
+
+            /**
+             * Gets the value of the title property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getTitle() {
+                return title;
+            }
+
+            /**
+             * Sets the value of the title property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setTitle(String value) {
+                this.title = value;
+            }
+
+            /**
+             * Gets the value of the viewId property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getViewId() {
+                return viewId;
+            }
+
+            /**
+             * Sets the value of the viewId property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setViewId(String value) {
+                this.viewId = value;
+            }
+
+            /**
+             * Gets the value of the dividerLocation property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getDividerLocation() {
+                return dividerLocation;
+            }
+
+            /**
+             * Sets the value of the dividerLocation property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setDividerLocation(Integer value) {
+                this.dividerLocation = value;
+            }
+
+            /**
+             * Gets the value of the selectedRna property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getSelectedRna() {
+                return selectedRna;
+            }
+
+            /**
+             * Sets the value of the selectedRna property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setSelectedRna(Integer value) {
+                this.selectedRna = value;
+            }
+
+            /**
+             * Gets the value of the width property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getWidth() {
+                return width;
+            }
+
+            /**
+             * Sets the value of the width property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setWidth(Integer value) {
+                this.width = value;
+            }
+
+            /**
+             * Gets the value of the height property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getHeight() {
+                return height;
+            }
+
+            /**
+             * Sets the value of the height property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setHeight(Integer value) {
+                this.height = value;
+            }
+
+            /**
+             * Gets the value of the xpos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getXpos() {
+                return xpos;
+            }
+
+            /**
+             * Sets the value of the xpos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setXpos(Integer value) {
+                this.xpos = value;
+            }
+
+            /**
+             * Gets the value of the ypos property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getYpos() {
+                return ypos;
+            }
+
+            /**
+             * Sets the value of the ypos property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setYpos(Integer value) {
+                this.ypos = value;
+            }
+
+
+            /**
+             * <p>Java class for anonymous complex type.
+             * 
+             * <p>The following schema fragment specifies the expected content contained within this class.
+             * 
+             * <pre>
+             * &lt;complexType>
+             *   &lt;complexContent>
+             *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+             *       &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
+             *       &lt;attribute name="annotationId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+             *       &lt;attribute name="gapped" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+             *       &lt;attribute name="viewerState" type="{http://www.w3.org/2001/XMLSchema}string" />
+             *     &lt;/restriction>
+             *   &lt;/complexContent>
+             * &lt;/complexType>
+             * </pre>
+             * 
+             * 
+             */
+            @XmlAccessorType(XmlAccessType.FIELD)
+            @XmlType(name = "")
+            public static class SecondaryStructure {
+
+                @XmlAttribute(name = "title")
+                protected String title;
+                @XmlAttribute(name = "annotationId", required = true)
+                protected String annotationId;
+                @XmlAttribute(name = "gapped")
+                protected Boolean gapped;
+                @XmlAttribute(name = "viewerState")
+                protected String viewerState;
+
+                /**
+                 * Gets the value of the title property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getTitle() {
+                    return title;
+                }
+
+                /**
+                 * Sets the value of the title property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setTitle(String value) {
+                    this.title = value;
+                }
+
+                /**
+                 * Gets the value of the annotationId property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getAnnotationId() {
+                    return annotationId;
+                }
+
+                /**
+                 * Sets the value of the annotationId property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setAnnotationId(String value) {
+                    this.annotationId = value;
+                }
+
+                /**
+                 * Gets the value of the gapped property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public Boolean isGapped() {
+                    return gapped;
+                }
+
+                /**
+                 * Sets the value of the gapped property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link Boolean }
+                 *     
+                 */
+                public void setGapped(Boolean value) {
+                    this.gapped = value;
+                }
+
+                /**
+                 * Gets the value of the viewerState property.
+                 * 
+                 * @return
+                 *     possible object is
+                 *     {@link String }
+                 *     
+                 */
+                public String getViewerState() {
+                    return viewerState;
+                }
+
+                /**
+                 * Sets the value of the viewerState property.
+                 * 
+                 * @param value
+                 *     allowed object is
+                 *     {@link String }
+                 *     
+                 */
+                public void setViewerState(String value) {
+                    this.viewerState = value;
+                }
+
+            }
+
+        }
+
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * &lt;complexType>
+     *   &lt;complexContent>
+     *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       &lt;sequence minOccurs="0">
+     *         &lt;element name="title" type="{http://www.w3.org/2001/XMLSchema}string"/>
+     *         &lt;element name="newick" type="{http://www.w3.org/2001/XMLSchema}string"/>
+     *       &lt;/sequence>
+     *       &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+     *       &lt;attribute name="fontName" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="fontSize" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="fontStyle" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" />
+     *       &lt;attribute name="showBootstrap" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="showDistances" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="markUnlinked" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="fitToWindow" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="currentTree" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" />
+     *     &lt;/restriction>
+     *   &lt;/complexContent>
+     * &lt;/complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "title",
+        "newick"
+    })
+    public static class Tree {
+
+        @XmlElement(namespace = "www.jalview.org")
+        protected String title;
+        @XmlElement(namespace = "www.jalview.org")
+        protected String newick;
+        @XmlAttribute(name = "fontName")
+        protected String fontName;
+        @XmlAttribute(name = "fontSize")
+        protected Integer fontSize;
+        @XmlAttribute(name = "fontStyle")
+        protected Integer fontStyle;
+        @XmlAttribute(name = "threshold")
+        protected Float threshold;
+        @XmlAttribute(name = "showBootstrap")
+        protected Boolean showBootstrap;
+        @XmlAttribute(name = "showDistances")
+        protected Boolean showDistances;
+        @XmlAttribute(name = "markUnlinked")
+        protected Boolean markUnlinked;
+        @XmlAttribute(name = "fitToWindow")
+        protected Boolean fitToWindow;
+        @XmlAttribute(name = "currentTree")
+        protected Boolean currentTree;
+        @XmlAttribute(name = "id")
+        @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+        @XmlID
+        @XmlSchemaType(name = "ID")
+        protected String id;
+        @XmlAttribute(name = "width")
+        protected Integer width;
+        @XmlAttribute(name = "height")
+        protected Integer height;
+        @XmlAttribute(name = "xpos")
+        protected Integer xpos;
+        @XmlAttribute(name = "ypos")
+        protected Integer ypos;
+
+        /**
+         * Gets the value of the title property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getTitle() {
+            return title;
+        }
+
+        /**
+         * Sets the value of the title property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setTitle(String value) {
+            this.title = value;
+        }
+
+        /**
+         * Gets the value of the newick property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getNewick() {
+            return newick;
+        }
+
+        /**
+         * Sets the value of the newick property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setNewick(String value) {
+            this.newick = value;
+        }
+
+        /**
+         * Gets the value of the fontName property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getFontName() {
+            return fontName;
+        }
+
+        /**
+         * Sets the value of the fontName property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setFontName(String value) {
+            this.fontName = value;
+        }
+
+        /**
+         * Gets the value of the fontSize property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getFontSize() {
+            return fontSize;
+        }
+
+        /**
+         * Sets the value of the fontSize property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setFontSize(Integer value) {
+            this.fontSize = value;
+        }
+
+        /**
+         * Gets the value of the fontStyle property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getFontStyle() {
+            return fontStyle;
+        }
+
+        /**
+         * Sets the value of the fontStyle property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setFontStyle(Integer value) {
+            this.fontStyle = value;
+        }
+
+        /**
+         * Gets the value of the threshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Float }
+         *     
+         */
+        public Float getThreshold() {
+            return threshold;
+        }
+
+        /**
+         * Sets the value of the threshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Float }
+         *     
+         */
+        public void setThreshold(Float value) {
+            this.threshold = value;
+        }
+
+        /**
+         * Gets the value of the showBootstrap property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowBootstrap() {
+            return showBootstrap;
+        }
+
+        /**
+         * Sets the value of the showBootstrap property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowBootstrap(Boolean value) {
+            this.showBootstrap = value;
+        }
+
+        /**
+         * Gets the value of the showDistances property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowDistances() {
+            return showDistances;
+        }
+
+        /**
+         * Sets the value of the showDistances property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowDistances(Boolean value) {
+            this.showDistances = value;
+        }
+
+        /**
+         * Gets the value of the markUnlinked property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isMarkUnlinked() {
+            return markUnlinked;
+        }
+
+        /**
+         * Sets the value of the markUnlinked property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setMarkUnlinked(Boolean value) {
+            this.markUnlinked = value;
+        }
+
+        /**
+         * Gets the value of the fitToWindow property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isFitToWindow() {
+            return fitToWindow;
+        }
+
+        /**
+         * Sets the value of the fitToWindow property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setFitToWindow(Boolean value) {
+            this.fitToWindow = value;
+        }
+
+        /**
+         * Gets the value of the currentTree property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isCurrentTree() {
+            return currentTree;
+        }
+
+        /**
+         * Sets the value of the currentTree property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setCurrentTree(Boolean value) {
+            this.currentTree = value;
+        }
+
+        /**
+         * Gets the value of the id property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getId() {
+            return id;
+        }
+
+        /**
+         * Sets the value of the id property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setId(String value) {
+            this.id = value;
+        }
+
+        /**
+         * Gets the value of the width property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getWidth() {
+            return width;
+        }
+
+        /**
+         * Sets the value of the width property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setWidth(Integer value) {
+            this.width = value;
+        }
+
+        /**
+         * Gets the value of the height property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getHeight() {
+            return height;
+        }
+
+        /**
+         * Sets the value of the height property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setHeight(Integer value) {
+            this.height = value;
+        }
+
+        /**
+         * Gets the value of the xpos property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getXpos() {
+            return xpos;
+        }
+
+        /**
+         * Sets the value of the xpos property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setXpos(Integer value) {
+            this.xpos = value;
+        }
+
+        /**
+         * Gets the value of the ypos property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getYpos() {
+            return ypos;
+        }
+
+        /**
+         * Sets the value of the ypos property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setYpos(Integer value) {
+            this.ypos = value;
+        }
+
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * &lt;complexType>
+     *   &lt;complexContent>
+     *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       &lt;sequence>
+     *         &lt;element name="UserColourScheme" type="{www.jalview.org/colours}JalviewUserColours"/>
+     *       &lt;/sequence>
+     *       &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *     &lt;/restriction>
+     *   &lt;/complexContent>
+     * &lt;/complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "userColourScheme"
+    })
+    public static class UserColours {
+
+        @XmlElement(name = "UserColourScheme", namespace = "www.jalview.org", required = true)
+        protected JalviewUserColours userColourScheme;
+        @XmlAttribute(name = "id")
+        protected String id;
+
+        /**
+         * Gets the value of the userColourScheme property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link JalviewUserColours }
+         *     
+         */
+        public JalviewUserColours getUserColourScheme() {
+            return userColourScheme;
+        }
+
+        /**
+         * Sets the value of the userColourScheme property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link JalviewUserColours }
+         *     
+         */
+        public void setUserColourScheme(JalviewUserColours value) {
+            this.userColourScheme = value;
+        }
+
+        /**
+         * Gets the value of the id property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getId() {
+            return id;
+        }
+
+        /**
+         * Sets the value of the id property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setId(String value) {
+            this.id = value;
+        }
+
+    }
+
+
+    /**
+     * <p>Java class for anonymous complex type.
+     * 
+     * <p>The following schema fragment specifies the expected content contained within this class.
+     * 
+     * <pre>
+     * &lt;complexType>
+     *   &lt;complexContent>
+     *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *       &lt;sequence>
+     *         &lt;element name="AnnotationColours" type="{www.jalview.org}AnnotationColourScheme" minOccurs="0"/>
+     *         &lt;element name="hiddenColumns" maxOccurs="unbounded" minOccurs="0">
+     *           &lt;complexType>
+     *             &lt;complexContent>
+     *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+     *                 &lt;attribute name="start" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *                 &lt;attribute name="end" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *               &lt;/restriction>
+     *             &lt;/complexContent>
+     *           &lt;/complexType>
+     *         &lt;/element>
+     *         &lt;element name="calcIdParam" maxOccurs="unbounded" minOccurs="0">
+     *           &lt;complexType>
+     *             &lt;complexContent>
+     *               &lt;extension base="{www.jalview.org/xml/wsparamset}WebServiceParameterSet">
+     *                 &lt;attribute name="calcId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *                 &lt;attribute name="needsUpdate" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *                 &lt;attribute name="autoUpdate" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *               &lt;/extension>
+     *             &lt;/complexContent>
+     *           &lt;/complexType>
+     *         &lt;/element>
+     *       &lt;/sequence>
+     *       &lt;attGroup ref="{www.jalview.org}swingwindow"/>
+     *       &lt;attribute name="conservationSelected" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="pidSelected" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="bgColour" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="consThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="pidThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="showFullId" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="rightAlignIds" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="showText" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="showColourText" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="showUnconserved" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *       &lt;attribute name="showBoxes" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="wrapAlignment" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="renderGaps" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="showSequenceFeatures" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="showNPfeatureTooltip" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="showDbRefTooltip" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="followHighlight" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+     *       &lt;attribute name="followSelection" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+     *       &lt;attribute name="showAnnotation" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="centreColumnLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *       &lt;attribute name="showGroupConservation" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *       &lt;attribute name="showGroupConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *       &lt;attribute name="showConsensusHistogram" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+     *       &lt;attribute name="showSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *       &lt;attribute name="normaliseSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+     *       &lt;attribute name="ignoreGapsinConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+     *       &lt;attribute name="startRes" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="startSeq" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="fontName" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="fontSize" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="fontStyle" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="scaleProteinAsCdna" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
+     *       &lt;attribute name="viewName" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="sequenceSetId" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="gatheredViews" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+     *       &lt;attribute name="textCol1" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="textCol2" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="textColThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
+     *       &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" />
+     *       &lt;attribute name="complementId" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *     &lt;/restriction>
+     *   &lt;/complexContent>
+     * &lt;/complexType>
+     * </pre>
+     * 
+     * 
+     */
+    @XmlAccessorType(XmlAccessType.FIELD)
+    @XmlType(name = "", propOrder = {
+        "annotationColours",
+        "hiddenColumns",
+        "calcIdParam"
+    })
+    public static class Viewport {
+
+        @XmlElement(name = "AnnotationColours", namespace = "www.jalview.org")
+        protected AnnotationColourScheme annotationColours;
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModelType.Viewport.HiddenColumns> hiddenColumns;
+        @XmlElement(namespace = "www.jalview.org")
+        protected List<JalviewModelType.Viewport.CalcIdParam> calcIdParam;
+        @XmlAttribute(name = "conservationSelected")
+        protected Boolean conservationSelected;
+        @XmlAttribute(name = "pidSelected")
+        protected Boolean pidSelected;
+        @XmlAttribute(name = "bgColour")
+        protected String bgColour;
+        @XmlAttribute(name = "consThreshold")
+        protected Integer consThreshold;
+        @XmlAttribute(name = "pidThreshold")
+        protected Integer pidThreshold;
+        @XmlAttribute(name = "title")
+        protected String title;
+        @XmlAttribute(name = "showFullId")
+        protected Boolean showFullId;
+        @XmlAttribute(name = "rightAlignIds")
+        protected Boolean rightAlignIds;
+        @XmlAttribute(name = "showText")
+        protected Boolean showText;
+        @XmlAttribute(name = "showColourText")
+        protected Boolean showColourText;
+        @XmlAttribute(name = "showUnconserved")
+        protected Boolean showUnconserved;
+        @XmlAttribute(name = "showBoxes")
+        protected Boolean showBoxes;
+        @XmlAttribute(name = "wrapAlignment")
+        protected Boolean wrapAlignment;
+        @XmlAttribute(name = "renderGaps")
+        protected Boolean renderGaps;
+        @XmlAttribute(name = "showSequenceFeatures")
+        protected Boolean showSequenceFeatures;
+        @XmlAttribute(name = "showNPfeatureTooltip")
+        protected Boolean showNPfeatureTooltip;
+        @XmlAttribute(name = "showDbRefTooltip")
+        protected Boolean showDbRefTooltip;
+        @XmlAttribute(name = "followHighlight")
+        protected Boolean followHighlight;
+        @XmlAttribute(name = "followSelection")
+        protected Boolean followSelection;
+        @XmlAttribute(name = "showAnnotation")
+        protected Boolean showAnnotation;
+        @XmlAttribute(name = "centreColumnLabels")
+        protected Boolean centreColumnLabels;
+        @XmlAttribute(name = "showGroupConservation")
+        protected Boolean showGroupConservation;
+        @XmlAttribute(name = "showGroupConsensus")
+        protected Boolean showGroupConsensus;
+        @XmlAttribute(name = "showConsensusHistogram")
+        protected Boolean showConsensusHistogram;
+        @XmlAttribute(name = "showSequenceLogo")
+        protected Boolean showSequenceLogo;
+        @XmlAttribute(name = "normaliseSequenceLogo")
+        protected Boolean normaliseSequenceLogo;
+        @XmlAttribute(name = "ignoreGapsinConsensus")
+        protected Boolean ignoreGapsinConsensus;
+        @XmlAttribute(name = "startRes")
+        protected Integer startRes;
+        @XmlAttribute(name = "startSeq")
+        protected Integer startSeq;
+        @XmlAttribute(name = "fontName")
+        protected String fontName;
+        @XmlAttribute(name = "fontSize")
+        protected Integer fontSize;
+        @XmlAttribute(name = "fontStyle")
+        protected Integer fontStyle;
+        @XmlAttribute(name = "scaleProteinAsCdna")
+        protected Boolean scaleProteinAsCdna;
+        @XmlAttribute(name = "viewName")
+        protected String viewName;
+        @XmlAttribute(name = "sequenceSetId")
+        protected String sequenceSetId;
+        @XmlAttribute(name = "gatheredViews")
+        protected Boolean gatheredViews;
+        @XmlAttribute(name = "textCol1")
+        protected Integer textCol1;
+        @XmlAttribute(name = "textCol2")
+        protected Integer textCol2;
+        @XmlAttribute(name = "textColThreshold")
+        protected Integer textColThreshold;
+        @XmlAttribute(name = "id")
+        @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+        @XmlID
+        @XmlSchemaType(name = "ID")
+        protected String id;
+        @XmlAttribute(name = "complementId")
+        protected String complementId;
+        @XmlAttribute(name = "width")
+        protected Integer width;
+        @XmlAttribute(name = "height")
+        protected Integer height;
+        @XmlAttribute(name = "xpos")
+        protected Integer xpos;
+        @XmlAttribute(name = "ypos")
+        protected Integer ypos;
+
+        /**
+         * Gets the value of the annotationColours property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link AnnotationColourScheme }
+         *     
+         */
+        public AnnotationColourScheme getAnnotationColours() {
+            return annotationColours;
+        }
+
+        /**
+         * Sets the value of the annotationColours property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link AnnotationColourScheme }
+         *     
+         */
+        public void setAnnotationColours(AnnotationColourScheme value) {
+            this.annotationColours = value;
+        }
+
+        /**
+         * Gets the value of the hiddenColumns property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the hiddenColumns property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getHiddenColumns().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModelType.Viewport.HiddenColumns }
+         * 
+         * 
+         */
+        public List<JalviewModelType.Viewport.HiddenColumns> getHiddenColumns() {
+            if (hiddenColumns == null) {
+                hiddenColumns = new ArrayList<JalviewModelType.Viewport.HiddenColumns>();
+            }
+            return this.hiddenColumns;
+        }
+
+        /**
+         * Gets the value of the calcIdParam property.
+         * 
+         * <p>
+         * This accessor method returns a reference to the live list,
+         * not a snapshot. Therefore any modification you make to the
+         * returned list will be present inside the JAXB object.
+         * This is why there is not a <CODE>set</CODE> method for the calcIdParam property.
+         * 
+         * <p>
+         * For example, to add a new item, do as follows:
+         * <pre>
+         *    getCalcIdParam().add(newItem);
+         * </pre>
+         * 
+         * 
+         * <p>
+         * Objects of the following type(s) are allowed in the list
+         * {@link JalviewModelType.Viewport.CalcIdParam }
+         * 
+         * 
+         */
+        public List<JalviewModelType.Viewport.CalcIdParam> getCalcIdParam() {
+            if (calcIdParam == null) {
+                calcIdParam = new ArrayList<JalviewModelType.Viewport.CalcIdParam>();
+            }
+            return this.calcIdParam;
+        }
+
+        /**
+         * Gets the value of the conservationSelected property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isConservationSelected() {
+            return conservationSelected;
+        }
+
+        /**
+         * Sets the value of the conservationSelected property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setConservationSelected(Boolean value) {
+            this.conservationSelected = value;
+        }
+
+        /**
+         * Gets the value of the pidSelected property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isPidSelected() {
+            return pidSelected;
+        }
+
+        /**
+         * Sets the value of the pidSelected property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setPidSelected(Boolean value) {
+            this.pidSelected = value;
+        }
+
+        /**
+         * Gets the value of the bgColour property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getBgColour() {
+            return bgColour;
+        }
+
+        /**
+         * Sets the value of the bgColour property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setBgColour(String value) {
+            this.bgColour = value;
+        }
+
+        /**
+         * Gets the value of the consThreshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getConsThreshold() {
+            return consThreshold;
+        }
+
+        /**
+         * Sets the value of the consThreshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setConsThreshold(Integer value) {
+            this.consThreshold = value;
+        }
+
+        /**
+         * Gets the value of the pidThreshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getPidThreshold() {
+            return pidThreshold;
+        }
+
+        /**
+         * Sets the value of the pidThreshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setPidThreshold(Integer value) {
+            this.pidThreshold = value;
+        }
+
+        /**
+         * Gets the value of the title property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getTitle() {
+            return title;
+        }
+
+        /**
+         * Sets the value of the title property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setTitle(String value) {
+            this.title = value;
+        }
+
+        /**
+         * Gets the value of the showFullId property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowFullId() {
+            return showFullId;
+        }
+
+        /**
+         * Sets the value of the showFullId property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowFullId(Boolean value) {
+            this.showFullId = value;
+        }
+
+        /**
+         * Gets the value of the rightAlignIds property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isRightAlignIds() {
+            return rightAlignIds;
+        }
+
+        /**
+         * Sets the value of the rightAlignIds property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setRightAlignIds(Boolean value) {
+            this.rightAlignIds = value;
+        }
+
+        /**
+         * Gets the value of the showText property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowText() {
+            return showText;
+        }
+
+        /**
+         * Sets the value of the showText property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowText(Boolean value) {
+            this.showText = value;
+        }
+
+        /**
+         * Gets the value of the showColourText property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowColourText() {
+            return showColourText;
+        }
+
+        /**
+         * Sets the value of the showColourText property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowColourText(Boolean value) {
+            this.showColourText = value;
+        }
+
+        /**
+         * Gets the value of the showUnconserved property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowUnconserved() {
+            if (showUnconserved == null) {
+                return false;
+            } else {
+                return showUnconserved;
+            }
+        }
+
+        /**
+         * Sets the value of the showUnconserved property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowUnconserved(Boolean value) {
+            this.showUnconserved = value;
+        }
+
+        /**
+         * Gets the value of the showBoxes property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowBoxes() {
+            return showBoxes;
+        }
+
+        /**
+         * Sets the value of the showBoxes property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowBoxes(Boolean value) {
+            this.showBoxes = value;
+        }
+
+        /**
+         * Gets the value of the wrapAlignment property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isWrapAlignment() {
+            return wrapAlignment;
+        }
+
+        /**
+         * Sets the value of the wrapAlignment property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setWrapAlignment(Boolean value) {
+            this.wrapAlignment = value;
+        }
+
+        /**
+         * Gets the value of the renderGaps property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isRenderGaps() {
+            return renderGaps;
+        }
+
+        /**
+         * Sets the value of the renderGaps property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setRenderGaps(Boolean value) {
+            this.renderGaps = value;
+        }
+
+        /**
+         * Gets the value of the showSequenceFeatures property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowSequenceFeatures() {
+            return showSequenceFeatures;
+        }
+
+        /**
+         * Sets the value of the showSequenceFeatures property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowSequenceFeatures(Boolean value) {
+            this.showSequenceFeatures = value;
+        }
+
+        /**
+         * Gets the value of the showNPfeatureTooltip property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowNPfeatureTooltip() {
+            return showNPfeatureTooltip;
+        }
+
+        /**
+         * Sets the value of the showNPfeatureTooltip property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowNPfeatureTooltip(Boolean value) {
+            this.showNPfeatureTooltip = value;
+        }
+
+        /**
+         * Gets the value of the showDbRefTooltip property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowDbRefTooltip() {
+            return showDbRefTooltip;
+        }
+
+        /**
+         * Sets the value of the showDbRefTooltip property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowDbRefTooltip(Boolean value) {
+            this.showDbRefTooltip = value;
+        }
+
+        /**
+         * Gets the value of the followHighlight property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isFollowHighlight() {
+            if (followHighlight == null) {
+                return true;
+            } else {
+                return followHighlight;
+            }
+        }
+
+        /**
+         * Sets the value of the followHighlight property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setFollowHighlight(Boolean value) {
+            this.followHighlight = value;
+        }
+
+        /**
+         * Gets the value of the followSelection property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isFollowSelection() {
+            if (followSelection == null) {
+                return true;
+            } else {
+                return followSelection;
+            }
+        }
+
+        /**
+         * Sets the value of the followSelection property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setFollowSelection(Boolean value) {
+            this.followSelection = value;
+        }
+
+        /**
+         * Gets the value of the showAnnotation property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isShowAnnotation() {
+            return showAnnotation;
+        }
+
+        /**
+         * Sets the value of the showAnnotation property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowAnnotation(Boolean value) {
+            this.showAnnotation = value;
+        }
+
+        /**
+         * Gets the value of the centreColumnLabels property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isCentreColumnLabels() {
+            if (centreColumnLabels == null) {
+                return false;
+            } else {
+                return centreColumnLabels;
+            }
+        }
+
+        /**
+         * Sets the value of the centreColumnLabels property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setCentreColumnLabels(Boolean value) {
+            this.centreColumnLabels = value;
+        }
+
+        /**
+         * Gets the value of the showGroupConservation property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowGroupConservation() {
+            if (showGroupConservation == null) {
+                return false;
+            } else {
+                return showGroupConservation;
+            }
+        }
+
+        /**
+         * Sets the value of the showGroupConservation property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowGroupConservation(Boolean value) {
+            this.showGroupConservation = value;
+        }
+
+        /**
+         * Gets the value of the showGroupConsensus property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowGroupConsensus() {
+            if (showGroupConsensus == null) {
+                return false;
+            } else {
+                return showGroupConsensus;
+            }
+        }
+
+        /**
+         * Sets the value of the showGroupConsensus property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowGroupConsensus(Boolean value) {
+            this.showGroupConsensus = value;
+        }
+
+        /**
+         * Gets the value of the showConsensusHistogram property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowConsensusHistogram() {
+            if (showConsensusHistogram == null) {
+                return true;
+            } else {
+                return showConsensusHistogram;
+            }
+        }
+
+        /**
+         * Sets the value of the showConsensusHistogram property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowConsensusHistogram(Boolean value) {
+            this.showConsensusHistogram = value;
+        }
+
+        /**
+         * Gets the value of the showSequenceLogo property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isShowSequenceLogo() {
+            if (showSequenceLogo == null) {
+                return false;
+            } else {
+                return showSequenceLogo;
+            }
+        }
+
+        /**
+         * Sets the value of the showSequenceLogo property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setShowSequenceLogo(Boolean value) {
+            this.showSequenceLogo = value;
+        }
+
+        /**
+         * Gets the value of the normaliseSequenceLogo property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isNormaliseSequenceLogo() {
+            if (normaliseSequenceLogo == null) {
+                return false;
+            } else {
+                return normaliseSequenceLogo;
+            }
+        }
+
+        /**
+         * Sets the value of the normaliseSequenceLogo property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setNormaliseSequenceLogo(Boolean value) {
+            this.normaliseSequenceLogo = value;
+        }
+
+        /**
+         * Gets the value of the ignoreGapsinConsensus property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isIgnoreGapsinConsensus() {
+            if (ignoreGapsinConsensus == null) {
+                return true;
+            } else {
+                return ignoreGapsinConsensus;
+            }
+        }
+
+        /**
+         * Sets the value of the ignoreGapsinConsensus property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setIgnoreGapsinConsensus(Boolean value) {
+            this.ignoreGapsinConsensus = value;
+        }
+
+        /**
+         * Gets the value of the startRes property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getStartRes() {
+            return startRes;
+        }
+
+        /**
+         * Sets the value of the startRes property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setStartRes(Integer value) {
+            this.startRes = value;
+        }
+
+        /**
+         * Gets the value of the startSeq property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getStartSeq() {
+            return startSeq;
+        }
+
+        /**
+         * Sets the value of the startSeq property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setStartSeq(Integer value) {
+            this.startSeq = value;
+        }
+
+        /**
+         * Gets the value of the fontName property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getFontName() {
+            return fontName;
+        }
+
+        /**
+         * Sets the value of the fontName property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setFontName(String value) {
+            this.fontName = value;
+        }
+
+        /**
+         * Gets the value of the fontSize property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getFontSize() {
+            return fontSize;
+        }
+
+        /**
+         * Sets the value of the fontSize property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setFontSize(Integer value) {
+            this.fontSize = value;
+        }
+
+        /**
+         * Gets the value of the fontStyle property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getFontStyle() {
+            return fontStyle;
+        }
+
+        /**
+         * Sets the value of the fontStyle property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setFontStyle(Integer value) {
+            this.fontStyle = value;
+        }
+
+        /**
+         * Gets the value of the scaleProteinAsCdna property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isScaleProteinAsCdna() {
+            if (scaleProteinAsCdna == null) {
+                return true;
+            } else {
+                return scaleProteinAsCdna;
+            }
+        }
+
+        /**
+         * Sets the value of the scaleProteinAsCdna property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setScaleProteinAsCdna(Boolean value) {
+            this.scaleProteinAsCdna = value;
+        }
+
+        /**
+         * Gets the value of the viewName property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getViewName() {
+            return viewName;
+        }
+
+        /**
+         * Sets the value of the viewName property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setViewName(String value) {
+            this.viewName = value;
+        }
+
+        /**
+         * Gets the value of the sequenceSetId property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getSequenceSetId() {
+            return sequenceSetId;
+        }
+
+        /**
+         * Sets the value of the sequenceSetId property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setSequenceSetId(String value) {
+            this.sequenceSetId = value;
+        }
+
+        /**
+         * Gets the value of the gatheredViews property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public Boolean isGatheredViews() {
+            return gatheredViews;
+        }
+
+        /**
+         * Sets the value of the gatheredViews property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setGatheredViews(Boolean value) {
+            this.gatheredViews = value;
+        }
+
+        /**
+         * Gets the value of the textCol1 property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextCol1() {
+            return textCol1;
+        }
+
+        /**
+         * Sets the value of the textCol1 property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextCol1(Integer value) {
+            this.textCol1 = value;
+        }
+
+        /**
+         * Gets the value of the textCol2 property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextCol2() {
+            return textCol2;
+        }
+
+        /**
+         * Sets the value of the textCol2 property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextCol2(Integer value) {
+            this.textCol2 = value;
+        }
+
+        /**
+         * Gets the value of the textColThreshold property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getTextColThreshold() {
+            return textColThreshold;
+        }
+
+        /**
+         * Sets the value of the textColThreshold property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setTextColThreshold(Integer value) {
+            this.textColThreshold = value;
+        }
+
+        /**
+         * Gets the value of the id property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getId() {
+            return id;
+        }
+
+        /**
+         * Sets the value of the id property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setId(String value) {
+            this.id = value;
+        }
+
+        /**
+         * Gets the value of the complementId property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link String }
+         *     
+         */
+        public String getComplementId() {
+            return complementId;
+        }
+
+        /**
+         * Sets the value of the complementId property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link String }
+         *     
+         */
+        public void setComplementId(String value) {
+            this.complementId = value;
+        }
+
+        /**
+         * Gets the value of the width property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getWidth() {
+            return width;
+        }
+
+        /**
+         * Sets the value of the width property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setWidth(Integer value) {
+            this.width = value;
+        }
+
+        /**
+         * Gets the value of the height property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getHeight() {
+            return height;
+        }
+
+        /**
+         * Sets the value of the height property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setHeight(Integer value) {
+            this.height = value;
+        }
+
+        /**
+         * Gets the value of the xpos property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getXpos() {
+            return xpos;
+        }
+
+        /**
+         * Sets the value of the xpos property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setXpos(Integer value) {
+            this.xpos = value;
+        }
+
+        /**
+         * Gets the value of the ypos property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Integer }
+         *     
+         */
+        public Integer getYpos() {
+            return ypos;
+        }
+
+        /**
+         * Sets the value of the ypos property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Integer }
+         *     
+         */
+        public void setYpos(Integer value) {
+            this.ypos = value;
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * &lt;complexType>
+         *   &lt;complexContent>
+         *     &lt;extension base="{www.jalview.org/xml/wsparamset}WebServiceParameterSet">
+         *       &lt;attribute name="calcId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+         *       &lt;attribute name="needsUpdate" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
+         *       &lt;attribute name="autoUpdate" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
+         *     &lt;/extension>
+         *   &lt;/complexContent>
+         * &lt;/complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class CalcIdParam
+            extends WebServiceParameterSet
+        {
+
+            @XmlAttribute(name = "calcId", required = true)
+            protected String calcId;
+            @XmlAttribute(name = "needsUpdate")
+            protected Boolean needsUpdate;
+            @XmlAttribute(name = "autoUpdate", required = true)
+            protected boolean autoUpdate;
+
+            /**
+             * Gets the value of the calcId property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link String }
+             *     
+             */
+            public String getCalcId() {
+                return calcId;
+            }
+
+            /**
+             * Sets the value of the calcId property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link String }
+             *     
+             */
+            public void setCalcId(String value) {
+                this.calcId = value;
+            }
+
+            /**
+             * Gets the value of the needsUpdate property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Boolean }
+             *     
+             */
+            public boolean isNeedsUpdate() {
+                if (needsUpdate == null) {
+                    return false;
+                } else {
+                    return needsUpdate;
+                }
+            }
+
+            /**
+             * Sets the value of the needsUpdate property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Boolean }
+             *     
+             */
+            public void setNeedsUpdate(Boolean value) {
+                this.needsUpdate = value;
+            }
+
+            /**
+             * Gets the value of the autoUpdate property.
+             * 
+             */
+            public boolean isAutoUpdate() {
+                return autoUpdate;
+            }
+
+            /**
+             * Sets the value of the autoUpdate property.
+             * 
+             */
+            public void setAutoUpdate(boolean value) {
+                this.autoUpdate = value;
+            }
+
+        }
+
+
+        /**
+         * <p>Java class for anonymous complex type.
+         * 
+         * <p>The following schema fragment specifies the expected content contained within this class.
+         * 
+         * <pre>
+         * &lt;complexType>
+         *   &lt;complexContent>
+         *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+         *       &lt;attribute name="start" type="{http://www.w3.org/2001/XMLSchema}int" />
+         *       &lt;attribute name="end" type="{http://www.w3.org/2001/XMLSchema}int" />
+         *     &lt;/restriction>
+         *   &lt;/complexContent>
+         * &lt;/complexType>
+         * </pre>
+         * 
+         * 
+         */
+        @XmlAccessorType(XmlAccessType.FIELD)
+        @XmlType(name = "")
+        public static class HiddenColumns {
+
+            @XmlAttribute(name = "start")
+            protected Integer start;
+            @XmlAttribute(name = "end")
+            protected Integer end;
+
+            /**
+             * Gets the value of the start property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getStart() {
+                return start;
+            }
+
+            /**
+             * Sets the value of the start property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setStart(Integer value) {
+                this.start = value;
+            }
+
+            /**
+             * Gets the value of the end property.
+             * 
+             * @return
+             *     possible object is
+             *     {@link Integer }
+             *     
+             */
+            public Integer getEnd() {
+                return end;
+            }
+
+            /**
+             * Sets the value of the end property.
+             * 
+             * @param value
+             *     allowed object is
+             *     {@link Integer }
+             *     
+             */
+            public void setEnd(Integer value) {
+                this.end = value;
+            }
+
+        }
+
+    }
+
+}
index 701647c..c43e04c 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 3d7b5eb..1a31d82 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 3cbebc0..5ebeb7e 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
@@ -10,6 +10,7 @@ package jalview.xml.binding.jalview;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
@@ -41,6 +42,7 @@ import javax.xml.bind.annotation.XmlType;
  *           &lt;/element>
  *         &lt;/choice>
  *       &lt;/sequence>
+ *       &lt;attribute name="mappingType" type="{http://www.w3.org/2001/XMLSchema}string" />
  *     &lt;/extension>
  *   &lt;/complexContent>
  * &lt;/complexType>
@@ -61,6 +63,8 @@ public class Mapping
     @XmlElement(name = "Sequence")
     protected Sequence sequence;
     protected String dseqFor;
+    @XmlAttribute(name = "mappingType")
+    protected String mappingType;
 
     /**
      * Gets the value of the sequence property.
@@ -110,4 +114,28 @@ public class Mapping
         this.dseqFor = value;
     }
 
+    /**
+     * Gets the value of the mappingType property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getMappingType() {
+        return mappingType;
+    }
+
+    /**
+     * Sets the value of the mappingType property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setMappingType(String value) {
+        this.mappingType = value;
+    }
+
 }
index 15fc45d..9db4ea3 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 6858f07..e0b2127 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index e8c7cf2..6234f32 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 843ea6c..d5132ab 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index abb40c9..b842947 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
@@ -38,12 +38,14 @@ import javax.xml.bind.annotation.XmlType;
  *                 &lt;attribute name="source" type="{http://www.w3.org/2001/XMLSchema}string" />
  *                 &lt;attribute name="version" type="{http://www.w3.org/2001/XMLSchema}string" />
  *                 &lt;attribute name="accessionId" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *                 &lt;attribute name="locus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
  *               &lt;/restriction>
  *             &lt;/complexContent>
  *           &lt;/complexType>
  *         &lt;/element>
  *       &lt;/sequence>
  *       &lt;attribute name="dsseqid" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       &lt;attribute name="biotype" type="{http://www.w3.org/2001/XMLSchema}string" />
  *     &lt;/extension>
  *   &lt;/complexContent>
  * &lt;/complexType>
@@ -64,6 +66,8 @@ public class Sequence
     protected List<Sequence.DBRef> dbRef;
     @XmlAttribute(name = "dsseqid")
     protected String dsseqid;
+    @XmlAttribute(name = "biotype")
+    protected String biotype;
 
     /**
      * Gets the value of the dbRef property.
@@ -118,6 +122,30 @@ public class Sequence
         this.dsseqid = value;
     }
 
+    /**
+     * Gets the value of the biotype property.
+     * 
+     * @return
+     *     possible object is
+     *     {@link String }
+     *     
+     */
+    public String getBiotype() {
+        return biotype;
+    }
+
+    /**
+     * Sets the value of the biotype property.
+     * 
+     * @param value
+     *     allowed object is
+     *     {@link String }
+     *     
+     */
+    public void setBiotype(String value) {
+        this.biotype = value;
+    }
+
 
     /**
      * <p>Java class for anonymous complex type.
@@ -134,6 +162,7 @@ public class Sequence
      *       &lt;attribute name="source" type="{http://www.w3.org/2001/XMLSchema}string" />
      *       &lt;attribute name="version" type="{http://www.w3.org/2001/XMLSchema}string" />
      *       &lt;attribute name="accessionId" type="{http://www.w3.org/2001/XMLSchema}string" />
+     *       &lt;attribute name="locus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
      *     &lt;/restriction>
      *   &lt;/complexContent>
      * &lt;/complexType>
@@ -155,6 +184,8 @@ public class Sequence
         protected String version;
         @XmlAttribute(name = "accessionId")
         protected String accessionId;
+        @XmlAttribute(name = "locus")
+        protected Boolean locus;
 
         /**
          * Gets the value of the mapping property.
@@ -252,6 +283,34 @@ public class Sequence
             this.accessionId = value;
         }
 
+        /**
+         * Gets the value of the locus property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isLocus() {
+            if (locus == null) {
+                return false;
+            } else {
+                return locus;
+            }
+        }
+
+        /**
+         * Sets the value of the locus property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setLocus(Boolean value) {
+            this.locus = value;
+        }
+
     }
 
 }
index 0fc7771..6aee6ac 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 07b8c24..aef7543 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index e8b7e28..1b3d6d4 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index 1f68de9..5d341c3 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index c884556..659eab9 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 
index bd6dc77..3ed532d 100644 (file)
@@ -2,7 +2,7 @@
 // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
 // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2018.12.20 at 11:47:26 AM GMT 
+// Generated on: 2019.06.07 at 02:21:15 PM BST 
 //
 
 @javax.xml.bind.annotation.XmlSchema(namespace = "www.vamsas.ac.uk/jalview/version2", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
index 313ec1c..fe6d962 100644 (file)
@@ -119,6 +119,7 @@ import java.util.Map;
  * 
  */
 public class EpsGraphics2D extends java.awt.Graphics2D
+        implements AutoCloseable
 {
 
   public static final String VERSION = "0.8.8";
@@ -250,6 +251,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * OutputStream is automatically flushed before being closed. If you forget to
    * do this, the file may be incomplete.
    */
+  @Override
   public void close() throws IOException
   {
     flush();
@@ -402,6 +404,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Draws a 3D rectangle outline. If it is raised, light appears to come from
    * the top left.
    */
+  @Override
   public void draw3DRect(int x, int y, int width, int height, boolean raised)
   {
     Color originalColor = getColor();
@@ -441,6 +444,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Fills a 3D rectangle. If raised, it has bright fill and light appears to
    * come from the top left.
    */
+  @Override
   public void fill3DRect(int x, int y, int width, int height, boolean raised)
   {
     Color originalColor = getColor();
@@ -461,6 +465,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a Shape on the EPS document.
    */
+  @Override
   public void draw(Shape s)
   {
     draw(s, "stroke");
@@ -469,6 +474,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws an Image on the EPS document.
    */
+  @Override
   public boolean drawImage(Image img, AffineTransform xform,
           ImageObserver obs)
   {
@@ -482,6 +488,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a BufferedImage on the EPS document.
    */
+  @Override
   public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y)
   {
     BufferedImage img1 = op.filter(img, null);
@@ -491,6 +498,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a RenderedImage on the EPS document.
    */
+  @Override
   public void drawRenderedImage(RenderedImage img, AffineTransform xform)
   {
     Hashtable properties = new Hashtable();
@@ -513,6 +521,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a RenderableImage by invoking its createDefaultRendering method.
    */
+  @Override
   public void drawRenderableImage(RenderableImage img, AffineTransform xform)
   {
     drawRenderedImage(img.createDefaultRendering(), xform);
@@ -521,6 +530,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a string at (x,y)
    */
+  @Override
   public void drawString(String str, int x, int y)
   {
     drawString(str, (float) x, (float) y);
@@ -529,6 +539,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a string at (x,y)
    */
+  @Override
   public void drawString(String s, float x, float y)
   {
     if (s != null && s.length() > 0)
@@ -543,6 +554,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Draws the characters of an AttributedCharacterIterator, starting from
    * (x,y).
    */
+  @Override
   public void drawString(AttributedCharacterIterator iterator, int x, int y)
   {
     drawString(iterator, (float) x, (float) y);
@@ -552,6 +564,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Draws the characters of an AttributedCharacterIterator, starting from
    * (x,y).
    */
+  @Override
   public void drawString(AttributedCharacterIterator iterator, float x,
           float y)
   {
@@ -584,6 +597,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a GlyphVector at (x,y)
    */
+  @Override
   public void drawGlyphVector(GlyphVector g, float x, float y)
   {
     Shape shape = g.getOutline(x, y);
@@ -593,6 +607,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Fills a Shape on the EPS document.
    */
+  @Override
   public void fill(Shape s)
   {
     draw(s, "fill");
@@ -602,6 +617,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Checks whether or not the specified Shape intersects the specified
    * Rectangle, which is in device space.
    */
+  @Override
   public boolean hit(Rectangle rect, Shape s, boolean onStroke)
   {
     return s.intersects(rect);
@@ -610,6 +626,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Returns the device configuration associated with this EpsGraphics2D object.
    */
+  @Override
   public GraphicsConfiguration getDeviceConfiguration()
   {
     GraphicsConfiguration gc = null;
@@ -632,6 +649,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Sets the Composite to be used by this EpsGraphics2D. EpsGraphics2D does not
    * make use of these.
    */
+  @Override
   public void setComposite(Composite comp)
   {
     _composite = comp;
@@ -641,6 +659,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Sets the Paint attribute for the EpsGraphics2D object. Only Paint objects
    * of type Color are respected by EpsGraphics2D.
    */
+  @Override
   public void setPaint(Paint paint)
   {
     _paint = paint;
@@ -654,6 +673,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Sets the stroke. Only accepts BasicStroke objects (or subclasses of
    * BasicStroke).
    */
+  @Override
   public void setStroke(Stroke s)
   {
     if (s instanceof BasicStroke)
@@ -688,6 +708,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Sets a rendering hint. These are not used by EpsGraphics2D.
    */
+  @Override
   public void setRenderingHint(RenderingHints.Key hintKey, Object hintValue)
   {
     // Do nothing.
@@ -697,6 +718,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Returns the value of a single preference for the rendering algorithms.
    * Rendering hints are not used by EpsGraphics2D.
    */
+  @Override
   public Object getRenderingHint(RenderingHints.Key hintKey)
   {
     return null;
@@ -705,6 +727,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Sets the rendering hints. These are ignored by EpsGraphics2D.
    */
+  @Override
   public void setRenderingHints(Map hints)
   {
     // Do nothing.
@@ -713,6 +736,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Adds rendering hints. These are ignored by EpsGraphics2D.
    */
+  @Override
   public void addRenderingHints(Map hints)
   {
     // Do nothing.
@@ -721,6 +745,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Returns the preferences for the rendering algorithms.
    */
+  @Override
   public RenderingHints getRenderingHints()
   {
     return new RenderingHints(null);
@@ -730,6 +755,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Translates the origin of the EpsGraphics2D context to the point (x,y) in
    * the current coordinate system.
    */
+  @Override
   public void translate(int x, int y)
   {
     translate((double) x, (double) y);
@@ -739,6 +765,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Concatenates the current EpsGraphics2D Transformation with a translation
    * transform.
    */
+  @Override
   public void translate(double tx, double ty)
   {
     transform(AffineTransform.getTranslateInstance(tx, ty));
@@ -747,6 +774,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Concatenates the current EpsGraphics2D Transform with a rotation transform.
    */
+  @Override
   public void rotate(double theta)
   {
     rotate(theta, 0, 0);
@@ -756,6 +784,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Concatenates the current EpsGraphics2D Transform with a translated rotation
    * transform.
    */
+  @Override
   public void rotate(double theta, double x, double y)
   {
     transform(AffineTransform.getRotateInstance(theta, x, y));
@@ -765,6 +794,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Concatenates the current EpsGraphics2D Transform with a scaling
    * transformation.
    */
+  @Override
   public void scale(double sx, double sy)
   {
     transform(AffineTransform.getScaleInstance(sx, sy));
@@ -773,6 +803,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Concatenates the current EpsGraphics2D Transform with a shearing transform.
    */
+  @Override
   public void shear(double shx, double shy)
   {
     transform(AffineTransform.getShearInstance(shx, shy));
@@ -782,6 +813,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Composes an AffineTransform object with the Transform in this EpsGraphics2D
    * according to the rule last-specified-first-applied.
    */
+  @Override
   public void transform(AffineTransform Tx)
   {
     _transform.concatenate(Tx);
@@ -791,6 +823,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Sets the AffineTransform to be used by this EpsGraphics2D.
    */
+  @Override
   public void setTransform(AffineTransform Tx)
   {
     if (Tx == null)
@@ -809,6 +842,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Gets the AffineTransform used by this EpsGraphics2D.
    */
+  @Override
   public AffineTransform getTransform()
   {
     return new AffineTransform(_transform);
@@ -817,6 +851,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Returns the current Paint of the EpsGraphics2D object.
    */
+  @Override
   public Paint getPaint()
   {
     return _paint;
@@ -825,6 +860,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * returns the current Composite of the EpsGraphics2D object.
    */
+  @Override
   public Composite getComposite()
   {
     return _composite;
@@ -833,6 +869,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Sets the background color to be used by the clearRect method.
    */
+  @Override
   public void setBackground(Color color)
   {
     if (color == null)
@@ -845,6 +882,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Gets the background color that is used by the clearRect method.
    */
+  @Override
   public Color getBackground()
   {
     return _backgroundColor;
@@ -854,6 +892,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Returns the Stroke currently used. Guaranteed to be an instance of
    * BasicStroke.
    */
+  @Override
   public Stroke getStroke()
   {
     return _stroke;
@@ -863,6 +902,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Intersects the current clip with the interior of the specified Shape and
    * sets the clip to the resulting intersection.
    */
+  @Override
   public void clip(Shape s)
   {
     if (_clip == null)
@@ -880,6 +920,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Returns the FontRenderContext.
    */
+  @Override
   public FontRenderContext getFontRenderContext()
   {
     return _fontRenderContext;
@@ -890,6 +931,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Returns a new Graphics object that is identical to this EpsGraphics2D.
    */
+  @Override
   public Graphics create()
   {
     return new EpsGraphics2D(this);
@@ -899,6 +941,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Returns an EpsGraphics2D object based on this Graphics object, but with a
    * new translation and clip area.
    */
+  @Override
   public Graphics create(int x, int y, int width, int height)
   {
     Graphics g = create();
@@ -911,6 +954,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Returns the current Color. This will be a default value (black) until it is
    * changed using the setColor method.
    */
+  @Override
   public Color getColor()
   {
     return _color;
@@ -919,6 +963,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Sets the Color to be used when drawing all future shapes, text, etc.
    */
+  @Override
   public void setColor(Color c)
   {
     if (c == null)
@@ -934,6 +979,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Sets the paint mode of this EpsGraphics2D object to overwrite the
    * destination EpsDocument with the current color.
    */
+  @Override
   public void setPaintMode()
   {
     // Do nothing - paint mode is the only method supported anyway.
@@ -943,6 +989,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * <b><i><font color="red">Not implemented</font></i></b> - performs no
    * action.
    */
+  @Override
   public void setXORMode(Color c1)
   {
     methodNotSupported();
@@ -951,6 +998,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Returns the Font currently being used.
    */
+  @Override
   public Font getFont()
   {
     return _font;
@@ -959,6 +1007,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Sets the Font to be used in future text.
    */
+  @Override
   public void setFont(Font font)
   {
     if (font == null)
@@ -966,13 +1015,14 @@ public class EpsGraphics2D extends java.awt.Graphics2D
       font = Font.decode(null);
     }
     _font = font;
-    append("/" + _font.getPSName() + " findfont " + ((int) _font.getSize())
+    append("/" + _font.getPSName() + " findfont " + (_font.getSize())
             + " scalefont setfont");
   }
 
   /**
    * Gets the font metrics of the current font.
    */
+  @Override
   public FontMetrics getFontMetrics()
   {
     return getFontMetrics(getFont());
@@ -981,6 +1031,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Gets the font metrics for the specified font.
    */
+  @Override
   public FontMetrics getFontMetrics(Font f)
   {
     BufferedImage image = new BufferedImage(1, 1,
@@ -992,6 +1043,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Returns the bounding rectangle of the current clipping area.
    */
+  @Override
   public Rectangle getClipBounds()
   {
     if (_clip == null)
@@ -1005,6 +1057,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Intersects the current clip with the specified rectangle.
    */
+  @Override
   public void clipRect(int x, int y, int width, int height)
   {
     clip(new Rectangle(x, y, width, height));
@@ -1013,6 +1066,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Sets the current clip to the rectangle specified by the given coordinates.
    */
+  @Override
   public void setClip(int x, int y, int width, int height)
   {
     setClip(new Rectangle(x, y, width, height));
@@ -1021,6 +1075,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Gets the current clipping area.
    */
+  @Override
   public Shape getClip()
   {
     if (_clip == null)
@@ -1046,6 +1101,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Sets the current clipping area to an arbitrary clip shape.
    */
+  @Override
   public void setClip(Shape clip)
   {
     if (clip != null)
@@ -1079,6 +1135,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * <b><i><font color="red">Not implemented</font></i></b> - performs no
    * action.
    */
+  @Override
   public void copyArea(int x, int y, int width, int height, int dx, int dy)
   {
     methodNotSupported();
@@ -1087,6 +1144,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a straight line from (x1,y1) to (x2,y2).
    */
+  @Override
   public void drawLine(int x1, int y1, int x2, int y2)
   {
     Shape shape = new Line2D.Float(x1, y1, x2, y2);
@@ -1096,6 +1154,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Fills a rectangle with top-left corner placed at (x,y).
    */
+  @Override
   public void fillRect(int x, int y, int width, int height)
   {
     Shape shape = new Rectangle(x, y, width, height);
@@ -1105,6 +1164,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a rectangle with top-left corner placed at (x,y).
    */
+  @Override
   public void drawRect(int x, int y, int width, int height)
   {
     Shape shape = new Rectangle(x, y, width, height);
@@ -1115,6 +1175,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Clears a rectangle with top-left corner placed at (x,y) using the current
    * background color.
    */
+  @Override
   public void clearRect(int x, int y, int width, int height)
   {
     Color originalColor = getColor();
@@ -1129,6 +1190,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a rounded rectangle.
    */
+  @Override
   public void drawRoundRect(int x, int y, int width, int height,
           int arcWidth, int arcHeight)
   {
@@ -1140,6 +1202,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Fills a rounded rectangle.
    */
+  @Override
   public void fillRoundRect(int x, int y, int width, int height,
           int arcWidth, int arcHeight)
   {
@@ -1151,6 +1214,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws an oval.
    */
+  @Override
   public void drawOval(int x, int y, int width, int height)
   {
     Shape shape = new Ellipse2D.Float(x, y, width, height);
@@ -1160,6 +1224,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Fills an oval.
    */
+  @Override
   public void fillOval(int x, int y, int width, int height)
   {
     Shape shape = new Ellipse2D.Float(x, y, width, height);
@@ -1169,6 +1234,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws an arc.
    */
+  @Override
   public void drawArc(int x, int y, int width, int height, int startAngle,
           int arcAngle)
   {
@@ -1180,6 +1246,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Fills an arc.
    */
+  @Override
   public void fillArc(int x, int y, int width, int height, int startAngle,
           int arcAngle)
   {
@@ -1191,6 +1258,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a polyline.
    */
+  @Override
   public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints)
   {
     if (nPoints > 0)
@@ -1208,6 +1276,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a polygon made with the specified points.
    */
+  @Override
   public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
   {
     Shape shape = new Polygon(xPoints, yPoints, nPoints);
@@ -1217,6 +1286,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws a polygon.
    */
+  @Override
   public void drawPolygon(Polygon p)
   {
     draw(p);
@@ -1225,6 +1295,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Fills a polygon made with the specified points.
    */
+  @Override
   public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints)
   {
     Shape shape = new Polygon(xPoints, yPoints, nPoints);
@@ -1234,6 +1305,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Fills a polygon.
    */
+  @Override
   public void fillPolygon(Polygon p)
   {
     draw(p, "fill");
@@ -1242,6 +1314,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws the specified characters, starting from (x,y)
    */
+  @Override
   public void drawChars(char[] data, int offset, int length, int x, int y)
   {
     String string = new String(data, offset, length);
@@ -1251,6 +1324,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws the specified bytes, starting from (x,y)
    */
+  @Override
   public void drawBytes(byte[] data, int offset, int length, int x, int y)
   {
     String string = new String(data, offset, length);
@@ -1260,6 +1334,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws an image.
    */
+  @Override
   public boolean drawImage(Image img, int x, int y, ImageObserver observer)
   {
     return drawImage(img, x, y, Color.white, observer);
@@ -1268,6 +1343,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws an image.
    */
+  @Override
   public boolean drawImage(Image img, int x, int y, int width, int height,
           ImageObserver observer)
   {
@@ -1277,6 +1353,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws an image.
    */
+  @Override
   public boolean drawImage(Image img, int x, int y, Color bgcolor,
           ImageObserver observer)
   {
@@ -1288,6 +1365,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws an image.
    */
+  @Override
   public boolean drawImage(Image img, int x, int y, int width, int height,
           Color bgcolor, ImageObserver observer)
   {
@@ -1298,6 +1376,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws an image.
    */
+  @Override
   public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
           int sx1, int sy1, int sx2, int sy2, ImageObserver observer)
   {
@@ -1308,6 +1387,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Draws an image.
    */
+  @Override
   public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2,
           int sx1, int sy1, int sx2, int sy2, Color bgcolor,
           ImageObserver observer)
@@ -1403,24 +1483,29 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * only remaining EpsGraphics2D instance pointing at a EpsDocument object,
    * then the EpsDocument object shall become eligible for garbage collection.
    */
+  @Override
   public void dispose()
   {
     _document = null;
   }
 
+  /* bsoares 2019-03-20
+   * finalize is now deprecated. Implementing AutoCloseable instead
   /**
    * Finalizes the object.
-   */
+  @Override
   public void finalize()
   {
     super.finalize();
   }
+   */
 
   /**
    * Returns the entire contents of the EPS document, complete with headers and
    * bounding box. The returned String is suitable for being written directly to
    * disk as an EPS file.
    */
+  @Override
   public String toString()
   {
     StringWriter writer = new StringWriter();
@@ -1440,6 +1525,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
    * Returns true if the specified rectangular area might intersect the current
    * clipping area.
    */
+  @Override
   public boolean hitClip(int x, int y, int width, int height)
   {
     if (_clip == null)
@@ -1453,6 +1539,7 @@ public class EpsGraphics2D extends java.awt.Graphics2D
   /**
    * Returns the bounding rectangle of the current clipping area.
    */
+  @Override
   public Rectangle getClipBounds(Rectangle r)
   {
     if (_clip == null)
index fe1c70c..ba8f8b0 100755 (executable)
@@ -296,8 +296,8 @@ public class InputParams implements java.io.Serializable
     {
       _hashCode += getMatrix().hashCode();
     }
-    _hashCode += new Float(getExp()).hashCode();
-    _hashCode += new Boolean(isEchofilter()).hashCode();
+    _hashCode += Float.valueOf(getExp()).hashCode();
+    _hashCode += Boolean.valueOf(isEchofilter()).hashCode();
     if (getFilter() != null)
     {
       _hashCode += getFilter().hashCode();
@@ -325,7 +325,7 @@ public class InputParams implements java.io.Serializable
       _hashCode += getOutformat().hashCode();
     }
     _hashCode += getTopcombon();
-    _hashCode += new Boolean(isAsync()).hashCode();
+    _hashCode += Boolean.valueOf(isAsync()).hashCode();
     if (getEmail() != null)
     {
       _hashCode += getEmail().hashCode();
index 5391221..f06a80a 100644 (file)
@@ -285,7 +285,7 @@ public class AccessionMapperBindingStub extends org.apache.axis.client.Stub
     setAttachments(_call);
     java.lang.Object _resp = _call.invoke(new java.lang.Object[] {
         sequence, searchDatabases, taxonId,
-        new java.lang.Boolean(onlyActive) });
+        java.lang.Boolean.valueOf(onlyActive) });
 
     if (_resp instanceof java.rmi.RemoteException)
     {
@@ -332,7 +332,7 @@ public class AccessionMapperBindingStub extends org.apache.axis.client.Stub
     setAttachments(_call);
     java.lang.Object _resp = _call.invoke(new java.lang.Object[] {
         accession, ac_version, searchDatabases, taxonId,
-        new java.lang.Boolean(onlyActive) });
+        java.lang.Boolean.valueOf(onlyActive) });
 
     if (_resp instanceof java.rmi.RemoteException)
     {
index cc438be..d54ff8a 100644 (file)
@@ -2685,7 +2685,7 @@ public class AlignmentUtilsTests
      * transcript 'CDS' is 10-16, 17-21
      * which is 'gene' 158-164, 210-214
      */
-    MapList toMap = toLoci.getMap();
+    MapList toMap = toLoci.getMapping();
     assertEquals(1, toMap.getFromRanges().size());
     assertEquals(2, toMap.getFromRanges().get(0).length);
     assertEquals(1, toMap.getFromRanges().get(0)[0]);
@@ -2708,7 +2708,7 @@ public class AlignmentUtilsTests
     AlignmentUtils.transferGeneLoci(from, map, to);
     assertEquals("GRCh38", toLoci.getAssemblyId());
     assertEquals("7", toLoci.getChromosomeId());
-    toMap = toLoci.getMap();
+    toMap = toLoci.getMapping();
     assertEquals("[ [1, 12] ] 1:1 to [ [158, 164] [210, 214] ]",
             toMap.toString());
   }
index c7b1d53..9d8a993 100644 (file)
@@ -29,6 +29,8 @@ import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 
 import org.testng.Assert;
@@ -39,6 +41,7 @@ import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ModuleRef;
 import io.github.classgraph.ScanResult;
 
 public class CommandLineOperations
@@ -51,7 +54,8 @@ public class CommandLineOperations
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
-  private static final int TEST_TIMEOUT = 9000; // Note longer timeout needed on
+  private static final int TEST_TIMEOUT = 9000; // Note longer timeout needed
+                                                // on
                                                 // full test run than on
                                                 // individual tests
 
@@ -123,6 +127,10 @@ public class CommandLineOperations
 
   private static String classpath = null;
 
+  private static String modules = null;
+
+  private static String java_exe = null;
+
   public synchronized static String getClassPath()
   {
     if (scanner == null)
@@ -130,6 +138,14 @@ public class CommandLineOperations
       scanner = new ClassGraph();
       ScanResult scan = scanner.scan();
       classpath = scan.getClasspath();
+      modules = "";
+      for (ModuleRef mr : scan.getModules())
+      {
+        modules.concat(mr.getName());
+      }
+      java_exe = System.getProperty("java.home") + File.separator + "bin"
+              + File.separator + "java";
+
     }
     while (classpath == null)
     {
@@ -150,9 +166,12 @@ public class CommandLineOperations
     // Note: JAL-3065 - don't include quotes for lib/* because the arguments are
     // not expanded by the shell
     String classpath = getClassPath();
-    String _cmd = "java "
+    String _cmd = java_exe + " "
             + (withAwt ? "-Djava.awt.headless=true" : "")
-            + " -classpath " + classpath + " jalview.bin.Jalview ";
+            + " -classpath " + classpath
+            + (modules.length() > 2 ? "--add-modules=\"" + modules + "\""
+                    : "")
+            + " jalview.bin.Jalview ";
     Process ls2_proc = null;
     Worker worker = null;
     try
@@ -183,6 +202,20 @@ public class CommandLineOperations
     return worker;
   }
 
+  @Test(groups = { "Functional" })
+  public void reportCurrentWorkingDirectory()
+  {
+    try
+    {
+      Path currentRelativePath = Paths.get("");
+      String s = currentRelativePath.toAbsolutePath().toString();
+      System.out.println("Test CWD is " + s);
+    } catch (Exception q)
+    {
+      q.printStackTrace();
+    }
+  }
+
   @BeforeTest(alwaysRun = true)
   public void initialize()
   {
@@ -241,7 +274,8 @@ public class CommandLineOperations
   }
 
   @Test(
-    groups = { "Functional" },
+    groups =
+    { "Functional", "testben" },
     dataProvider = "headlessModeOutputOperationsData")
   public void testHeadlessModeOutputOperations(String harg, String type,
           String fileName, boolean withAWT, int expectedMinFileSize,
@@ -262,11 +296,9 @@ public class CommandLineOperations
       worker.interrupt();
       Thread.currentThread().interrupt();
       worker.process.destroy();
-      Assert.fail("Jalview did not exit after "
-              + type
+      Assert.fail("Jalview did not exit after " + type
               + " generation (try running test again to verify - timeout at "
-              + SETUP_TIMEOUT + "ms). ["
-              + harg + "]");
+              + timeout + "ms). [" + harg + "]");
     }
     file.delete();
   }
@@ -304,54 +336,58 @@ public class CommandLineOperations
   @DataProvider(name = "headlessModeOutputOperationsData")
   public static Object[][] getHeadlessModeOutputParams()
   {
-    return new Object[][] {
-        { "nodisplay -open examples/uniref50.fa", " -eps",
-            "test/jalview/bin/test_uniref50_out.eps", true, MINFILESIZE_BIG,
-            TEST_TIMEOUT },
+    // JBPNote: I'm not clear why need to specify full path for output file
+    // when running tests on build server, but we will keep this patch for now
+    // since it works.
+    // https://issues.jalview.org/browse/JAL-1889?focusedCommentId=21609&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-21609
+    String workingDir = "test/jalview/bin/";
+    return new Object[][] { { "nodisplay -open examples/uniref50.fa",
+        " -eps", workingDir + "test_uniref50_out.eps", true,
+        MINFILESIZE_BIG, TEST_TIMEOUT },
         { "nodisplay -open examples/uniref50.fa", " -eps",
-            "test/jalview/bin/test_uniref50_out.eps", false,
+            workingDir + "test_uniref50_out.eps", false,
             MINFILESIZE_BIG, TEST_TIMEOUT },
         { "nogui -open examples/uniref50.fa", " -eps",
-            "test/jalview/bin/test_uniref50_out.eps", true, MINFILESIZE_BIG,
+            workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG,
             TEST_TIMEOUT },
         { "nogui -open examples/uniref50.fa", " -eps",
-            "test/jalview/bin/test_uniref50_out.eps", false,
+            workingDir + "test_uniref50_out.eps", false,
             MINFILESIZE_BIG, TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -eps",
-            "test/jalview/bin/test_uniref50_out.eps", true, MINFILESIZE_BIG,
+            workingDir + "test_uniref50_out.eps", true, MINFILESIZE_BIG,
             TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -svg",
-            "test/jalview/bin/test_uniref50_out.svg", false,
+            workingDir + "test_uniref50_out.svg", false,
             MINFILESIZE_BIG, TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -png",
-            "test/jalview/bin/test_uniref50_out.png", true, MINFILESIZE_BIG,
+            workingDir + "test_uniref50_out.png", true, MINFILESIZE_BIG,
             TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -html",
-            "test/jalview/bin/test_uniref50_out.html", true,
+            workingDir + "test_uniref50_out.html", true,
             MINFILESIZE_BIG, TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -fasta",
-            "test/jalview/bin/test_uniref50_out.mfa", true,
-            MINFILESIZE_SMALL, TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.mfa", true, MINFILESIZE_SMALL,
+            TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -clustal",
-            "test/jalview/bin/test_uniref50_out.aln", true,
-            MINFILESIZE_SMALL, TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.aln", true, MINFILESIZE_SMALL,
+            TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -msf",
-            "test/jalview/bin/test_uniref50_out.msf", true,
-            MINFILESIZE_SMALL, TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.msf", true, MINFILESIZE_SMALL,
+            TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -pileup",
-            "test/jalview/bin/test_uniref50_out.aln", true,
-            MINFILESIZE_SMALL, TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.aln", true, MINFILESIZE_SMALL,
+            TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -pir",
-            "test/jalview/bin/test_uniref50_out.pir", true,
-            MINFILESIZE_SMALL, TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.pir", true, MINFILESIZE_SMALL,
+            TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -pfam",
-            "test/jalview/bin/test_uniref50_out.pfam", true,
-            MINFILESIZE_SMALL, TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.pfam", true, MINFILESIZE_SMALL,
+            TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -blc",
-            "test/jalview/bin/test_uniref50_out.blc", true,
-            MINFILESIZE_SMALL, TEST_TIMEOUT },
+            workingDir + "test_uniref50_out.blc", true, MINFILESIZE_SMALL,
+            TEST_TIMEOUT },
         { "headless -open examples/uniref50.fa", " -jalview",
-            "test/jalview/bin/test_uniref50_out.jvp", true,
-            MINFILESIZE_SMALL, TEST_TIMEOUT }, };
+            workingDir + "test_uniref50_out.jvp", true, MINFILESIZE_SMALL,
+            TEST_TIMEOUT }, };
   }
 }
index 2160657..348d871 100644 (file)
@@ -90,11 +90,11 @@ public class EditCommandTest
     testee = new EditCommand();
     seqs = new SequenceI[4];
     seqs[0] = new Sequence("seq0", "abcdefghjk");
-    seqs[0].setDatasetSequence(new Sequence("seq0ds", "abcdefghjk"));
+    seqs[0].setDatasetSequence(new Sequence("seq0ds", "ABCDEFGHJK"));
     seqs[1] = new Sequence("seq1", "fghjklmnopq");
-    seqs[1].setDatasetSequence(new Sequence("seq1ds", "fghjklmnopq"));
+    seqs[1].setDatasetSequence(new Sequence("seq1ds", "FGHJKLMNOPQ"));
     seqs[2] = new Sequence("seq2", "qrstuvwxyz");
-    seqs[2].setDatasetSequence(new Sequence("seq2ds", "qrstuvwxyz"));
+    seqs[2].setDatasetSequence(new Sequence("seq2ds", "QRSTUVWXYZ"));
     seqs[3] = new Sequence("seq3", "1234567890");
     seqs[3].setDatasetSequence(new Sequence("seq3ds", "1234567890"));
     al = new Alignment(seqs);
@@ -285,7 +285,8 @@ public class EditCommandTest
             4, 8, al);
     assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
     assertEquals("fghjZ-xYopq", seqs[1].getSequenceAsString());
-    assertEquals("fghjZxYopq",
+    // Dataset Sequence should always be uppercase
+    assertEquals("fghjZxYopq".toUpperCase(),
             seqs[1].getDatasetSequence().getSequenceAsString());
     assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
     assertEquals("1234567890", seqs[3].getSequenceAsString());
@@ -315,7 +316,8 @@ public class EditCommandTest
     assertEquals("ABxyZ-DEF", seq.getSequenceAsString());
     assertEquals(1, seq.getStart());
     assertEquals(8, seq.getEnd());
-    assertEquals("ABxyZDEF",
+    // Dataset sequence always uppercase
+    assertEquals("ABxyZDEF".toUpperCase(),
             seq.getDatasetSequence().getSequenceAsString());
     assertEquals(8, seq.getDatasetSequence().getEnd());
 
@@ -340,7 +342,8 @@ public class EditCommandTest
     assertEquals("ABxyZ-DEF", seq.getSequenceAsString());
     assertEquals(1, seq.getStart());
     assertEquals(8, seq.getEnd());
-    assertEquals("ABxyZDEF",
+    // dataset sequence should be Uppercase
+    assertEquals("ABxyZDEF".toUpperCase(),
             seq.getDatasetSequence().getSequenceAsString());
     assertEquals(8, seq.getDatasetSequence().getEnd());
 
@@ -363,8 +366,8 @@ public class EditCommandTest
     assertEquals("----klmnopq", seqs[1].getSequenceAsString());
     // and ds is preserved
     assertTrue(dsseq == seqs[1].getDatasetSequence());
-    // and it is unchanged
-    assertEquals("fghjklmnopq", dsseq.getSequenceAsString());
+    // and it is unchanged and UPPERCASE !
+    assertEquals("fghjklmnopq".toUpperCase(), dsseq.getSequenceAsString());
     // and that alignment sequence start has been adjusted
     assertEquals(5, seqs[1].getStart());
     assertEquals(11, seqs[1].getEnd());
@@ -390,8 +393,8 @@ public class EditCommandTest
     assertEquals("----klmnopq", seqs[1].getSequenceAsString());
     // and ds is preserved
     assertTrue(dsseq == seqs[1].getDatasetSequence());
-    // and it is unchanged
-    assertEquals("fghjklmnopq", dsseq.getSequenceAsString());
+    // and it is unchanged AND UPPERCASE !
+    assertEquals("fghjklmnopq".toUpperCase(), dsseq.getSequenceAsString());
     // and that alignment sequence start has been adjusted
     assertEquals(5, seqs[1].getStart());
     assertEquals(11, seqs[1].getEnd());
index 2dda4d3..aefcbc0 100644 (file)
@@ -103,8 +103,8 @@ public class ColumnSelectionTest
     cs.removeElement(1);
     List<Integer> sel = cs.getSelected();
     assertEquals(2, sel.size());
-    assertEquals(new Integer(2), sel.get(0));
-    assertEquals(new Integer(5), sel.get(1));
+    assertEquals(Integer.valueOf(2), sel.get(0));
+    assertEquals(Integer.valueOf(5), sel.get(1));
 
     // removing an element in the list removes it
     cs.removeElement(2);
@@ -112,7 +112,7 @@ public class ColumnSelectionTest
     assertEquals(1, sel.size());
     sel = cs.getSelected();
     assertEquals(1, sel.size());
-    assertEquals(new Integer(5), sel.get(0));
+    assertEquals(Integer.valueOf(5), sel.get(0));
   }
 
   /**
index c955979..9111e5a 100644 (file)
@@ -48,7 +48,7 @@ public class SequenceFeatureTest
             12.5f, "group");
     sf1.setValue("STRAND", "+");
     sf1.setValue("Note", "Testing");
-    Integer count = new Integer(7);
+    Integer count = Integer.valueOf(7);
     sf1.setValue("Count", count);
 
     SequenceFeature sf2 = new SequenceFeature(sf1);
@@ -106,7 +106,7 @@ public class SequenceFeatureTest
     assertEquals("+", sf1.getValue("STRAND"));
     assertNull(sf1.getValue("strand")); // case-sensitive
     assertEquals(".", sf1.getValue("unknown", "."));
-    Integer i = new Integer(27);
+    Integer i = Integer.valueOf(27);
     assertSame(i, sf1.getValue("Unknown", i));
   }
 
index 454ff61..3e82547 100644 (file)
@@ -62,7 +62,53 @@ public class AlignFrameTest
   AlignFrame af;
 
   @BeforeClass(alwaysRun = true)
-  public void setUpJvOptionPane()
+  public static void setUpBeforeClass() throws Exception
+  {
+    setUpJvOptionPane();
+    /*
+     * use read-only test properties file
+     */
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+    Jalview.main(new String[] { "-nonews" });
+  }
+
+  @AfterMethod(alwaysRun = true)
+  public void tearDown()
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+  }
+
+  /**
+   * configure (read-only) properties for test to ensure Consensus is computed for
+   * colour Above PID testing
+   */
+  @BeforeMethod(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.loadProperties("test/jalview/io/testProps.jvprops");
+    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
+            Boolean.TRUE.toString());
+    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
+            DataSourceType.FILE);
+
+    /*
+     * wait for Consensus thread to complete
+     */
+    synchronized (this)
+    {
+      while (af.getViewport().getConsensusSeq() == null)
+      {
+        try
+        {
+          wait(50);
+        } catch (InterruptedException e)
+        {
+        }
+      }
+    }
+  }
+
+  public static void setUpJvOptionPane()
   {
     JvOptionPane.setInteractiveMode(false);
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
@@ -151,52 +197,6 @@ public class AlignFrameTest
     assertEquals(next[1], 8);
   }
 
-  @BeforeClass(alwaysRun = true)
-  public static void setUpBeforeClass() throws Exception
-  {
-    /*
-     * use read-only test properties file
-     */
-    Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Jalview.main(new String[] { "-nonews" });
-  }
-
-  @AfterMethod(alwaysRun = true)
-  public void tearDown()
-  {
-    Desktop.instance.closeAll_actionPerformed(null);
-  }
-
-  /**
-   * configure (read-only) properties for test to ensure Consensus is computed
-   * for colour Above PID testing
-   */
-  @BeforeMethod(alwaysRun = true)
-  public void setUp()
-  {
-    Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Cache.applicationProperties.setProperty("SHOW_IDENTITY",
-            Boolean.TRUE.toString());
-    af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
-            DataSourceType.FILE);
-
-    /*
-     * wait for Consensus thread to complete
-     */
-    synchronized (this)
-    {
-      while (af.getViewport().getConsensusSeq() == null)
-      {
-        try
-        {
-          wait(50);
-        } catch (InterruptedException e)
-        {
-        }
-      }
-    }
-  }
-
   /**
    * Test that changing background (alignment) colour scheme
    * <ul>
index 6783514..cd0b594 100644 (file)
@@ -39,8 +39,8 @@ import jalview.gui.SeqPanel.MousePos;
 import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
 import jalview.util.MessageManager;
+import jalview.viewmodel.ViewportRanges;
 
-import java.awt.Event;
 import java.awt.EventQueue;
 import java.awt.event.MouseEvent;
 import java.lang.reflect.InvocationTargetException;
@@ -241,7 +241,7 @@ public class SeqPanelTest
     /*
      * mouse at top left of unwrapped panel
      */
-    MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y,
+    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
             0, 0, 0, false, 0);
     MousePos pos = testee.findMousePosition(evt);
     assertEquals(pos.column, 0);
@@ -286,7 +286,7 @@ public class SeqPanelTest
      * mouse at top left of wrapped panel; there is a gap of charHeight
      * above the alignment
      */
-    MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y,
+    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
             0, 0, 0, false, 0);
     MousePos pos = testee.findMousePosition(evt);
     assertEquals(pos.column, 0);
@@ -297,7 +297,7 @@ public class SeqPanelTest
      * cursor at bottom of gap above
      */
     y = charHeight - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, -1);
@@ -307,7 +307,7 @@ public class SeqPanelTest
      * cursor over top of first sequence
      */
     y = charHeight;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 0);
@@ -317,7 +317,7 @@ public class SeqPanelTest
      * cursor at bottom of first sequence
      */
     y = 2 * charHeight - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 0);
@@ -327,7 +327,7 @@ public class SeqPanelTest
      * cursor at top of second sequence
      */
     y = 2 * charHeight;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 1);
@@ -337,7 +337,7 @@ public class SeqPanelTest
      * cursor at bottom of second sequence
      */
     y = 3 * charHeight - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 1);
@@ -347,7 +347,7 @@ public class SeqPanelTest
      * cursor at bottom of last sequence
      */
     y = charHeight * (1 + alignmentHeight) - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -358,7 +358,7 @@ public class SeqPanelTest
      * method reports index of nearest sequence above
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -368,7 +368,7 @@ public class SeqPanelTest
      * cursor still in the gap above annotations, now at the bottom of it
      */
     y += SeqCanvas.SEQS_ANNOTATION_GAP - 1; // 3-1 = 2
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -382,7 +382,7 @@ public class SeqPanelTest
        * cursor at the top of the n'th annotation  
        */
       y += 1;
-      evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
               false, 0);
       pos = testee.findMousePosition(evt);
       assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -392,7 +392,7 @@ public class SeqPanelTest
        * cursor at the bottom of the n'th annotation  
        */
       y += annotationRows[n].height - 1;
-      evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
               false, 0);
       pos = testee.findMousePosition(evt);
       assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -403,7 +403,7 @@ public class SeqPanelTest
      * cursor in gap between wrapped widths  
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, -1);
@@ -413,7 +413,7 @@ public class SeqPanelTest
      * cursor at bottom of gap between wrapped widths  
      */
     y += charHeight - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, -1);
@@ -423,7 +423,7 @@ public class SeqPanelTest
      * cursor at top of first sequence, second wrapped width  
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 0);
@@ -460,7 +460,7 @@ public class SeqPanelTest
      * mouse at top left of wrapped panel; there is a gap of charHeight
      * above the alignment
      */
-    MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y,
+    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
             0, 0, 0, false, 0);
     MousePos pos = testee.findMousePosition(evt);
     assertEquals(pos.column, 0);
@@ -472,7 +472,7 @@ public class SeqPanelTest
      * two charHeights including scale panel
      */
     y = 2 * charHeight - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, -1);
@@ -482,7 +482,7 @@ public class SeqPanelTest
      * cursor over top of first sequence
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 0);
@@ -492,7 +492,7 @@ public class SeqPanelTest
      * cursor at bottom of first sequence
      */
     y += charHeight - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 0);
@@ -502,7 +502,7 @@ public class SeqPanelTest
      * cursor at top of second sequence
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 1);
@@ -512,7 +512,7 @@ public class SeqPanelTest
      * cursor at bottom of second sequence
      */
     y += charHeight - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 1);
@@ -523,7 +523,7 @@ public class SeqPanelTest
      * (scale + gap + sequences)
      */
     y = charHeight * (2 + alignmentHeight) - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -533,7 +533,7 @@ public class SeqPanelTest
      * cursor below sequences, in 3-pixel gap above annotations
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -544,7 +544,7 @@ public class SeqPanelTest
      * method reports index of nearest sequence above  
      */
     y += SeqCanvas.SEQS_ANNOTATION_GAP - 1; // 3-1 = 2
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -557,7 +557,7 @@ public class SeqPanelTest
        * cursor at the top of the n'th annotation  
        */
       y += 1;
-      evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
               false, 0);
       pos = testee.findMousePosition(evt);
       assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -567,7 +567,7 @@ public class SeqPanelTest
        * cursor at the bottom of the n'th annotation  
        */
       y += annotationRows[n].height - 1;
-      evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+      evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
               false, 0);
       pos = testee.findMousePosition(evt);
       assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -578,7 +578,7 @@ public class SeqPanelTest
      * cursor in gap between wrapped widths  
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, -1);
@@ -588,7 +588,7 @@ public class SeqPanelTest
      * cursor at bottom of gap between wrapped widths  
      */
     y += charHeight - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, -1);
@@ -598,7 +598,7 @@ public class SeqPanelTest
      * cursor at top of scale, second wrapped width  
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, -1);
@@ -608,7 +608,7 @@ public class SeqPanelTest
      * cursor at bottom of scale, second wrapped width  
      */
     y += charHeight - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, -1);
@@ -618,7 +618,7 @@ public class SeqPanelTest
      * cursor at top of first sequence, second wrapped width  
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 0);
@@ -655,7 +655,7 @@ public class SeqPanelTest
      * mouse at top left of wrapped panel; there is a gap of charHeight
      * above the alignment
      */
-    MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y,
+    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y,
             0, 0, 0, false, 0);
     MousePos pos = testee.findMousePosition(evt);
     assertEquals(pos.column, 0);
@@ -666,7 +666,7 @@ public class SeqPanelTest
      * cursor over top of first sequence
      */
     y = charHeight;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 0);
@@ -676,7 +676,7 @@ public class SeqPanelTest
      * cursor at bottom of last sequence
      */
     y = charHeight * (1 + alignmentHeight) - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, alignmentHeight - 1);
@@ -686,7 +686,7 @@ public class SeqPanelTest
      * cursor below sequences, at top of charHeight gap between widths
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, -1);
@@ -696,7 +696,7 @@ public class SeqPanelTest
      * cursor below sequences, at top of charHeight gap between widths
      */
     y += charHeight - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, -1);
@@ -706,7 +706,7 @@ public class SeqPanelTest
      * cursor at the top of the first sequence, second width  
      */
     y += 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, y, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
             false, 0);
     pos = testee.findMousePosition(evt);
     assertEquals(pos.seqIndex, 0);
@@ -723,12 +723,13 @@ public class SeqPanelTest
     int x = 0;
     final int charWidth = alignFrame.getViewport().getCharWidth();
     assertTrue(charWidth > 0); // sanity check
-    assertEquals(alignFrame.getViewport().getRanges().getStartRes(), 0);
+    ViewportRanges ranges = alignFrame.getViewport().getRanges();
+    assertEquals(ranges.getStartRes(), 0);
 
     /*
      * mouse at top left of unwrapped panel
      */
-    MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0,
+    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
             0, 0, 0, false, 0);
     assertEquals(testee.findColumn(evt), 0);
     
@@ -736,7 +737,7 @@ public class SeqPanelTest
      * not quite one charWidth across
      */
     x = charWidth-1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
             0, 0, 0, false, 0);
     assertEquals(testee.findColumn(evt), 0);
 
@@ -744,7 +745,7 @@ public class SeqPanelTest
      * one charWidth across
      */
     x = charWidth;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
             false, 0);
     assertEquals(testee.findColumn(evt), 1);
 
@@ -752,7 +753,7 @@ public class SeqPanelTest
      * two charWidths across
      */
     x = 2 * charWidth;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
             false, 0);
     assertEquals(testee.findColumn(evt), 2);
 
@@ -760,12 +761,14 @@ public class SeqPanelTest
      * limited to last column of seqcanvas
      */
     x = 20000;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
             false, 0);
     SeqCanvas seqCanvas = alignFrame.alignPanel.getSeqPanel().seqCanvas;
     int w = seqCanvas.getWidth();
-    // limited to number of whole columns, base 0
-    int expected = w / charWidth - 1;
+    // limited to number of whole columns, base 0,
+    // and to end of visible range
+    int expected = w / charWidth;
+    expected = Math.min(expected, ranges.getEndRes());
     assertEquals(testee.findColumn(evt), expected);
 
     /*
@@ -774,7 +777,7 @@ public class SeqPanelTest
     alignFrame.getViewport().hideColumns(4, 9);
     x = 5 * charWidth + 2;
     // x is in 6th visible column, absolute column 12, or 11 base 0
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
             false, 0);
     assertEquals(testee.findColumn(evt), 11);
   }
@@ -799,7 +802,7 @@ public class SeqPanelTest
     /*
      * mouse at top left of wrapped panel, no West (left) scale
      */
-    MouseEvent evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0,
+    MouseEvent evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
             0, 0, 0, false, 0);
     assertEquals(testee.findColumn(evt), 0);
     
@@ -807,7 +810,7 @@ public class SeqPanelTest
      * not quite one charWidth across
      */
     x = charWidth-1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0,
             0, 0, 0, false, 0);
     assertEquals(testee.findColumn(evt), 0);
   
@@ -815,7 +818,7 @@ public class SeqPanelTest
      * one charWidth across
      */
     x = charWidth;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
             false, 0);
     assertEquals(testee.findColumn(evt), 1);
 
@@ -828,12 +831,12 @@ public class SeqPanelTest
     int labelWidth = (int) PA.getValue(seqCanvas, "labelWidthWest");
     assertTrue(labelWidth > 0);
     x = labelWidth - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
             false, 0);
     assertEquals(testee.findColumn(evt), -1);
 
     x = labelWidth;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
             false, 0);
     assertEquals(testee.findColumn(evt), 0);
 
@@ -843,7 +846,7 @@ public class SeqPanelTest
     int residuesWide = av.getRanges().getViewportWidth();
     assertTrue(residuesWide > 0);
     x = labelWidth + charWidth * residuesWide - 1;
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
             false, 0);
     assertEquals(testee.findColumn(evt), residuesWide - 1);
 
@@ -858,7 +861,7 @@ public class SeqPanelTest
     assertTrue(residuesWide2 > 0);
     assertTrue(residuesWide2 < residuesWide); // available width reduced
     x += 1; // just over left edge of scale right
-    evt = new MouseEvent(testee, Event.MOUSE_MOVE, 0L, 0, x, 0, 0, 0, 0,
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, 0, 0, 0, 0,
             false, 0);
     assertEquals(testee.findColumn(evt), -1);
     
index 723279d..64cf902 100644 (file)
@@ -34,7 +34,8 @@ public class BackupFilesTest
   }
 
   private static boolean actuallyDeleteTmpFiles = true;
-  private static String testDir = "examples";
+
+  private static String testDir = "test/jalview/io";
 
   private static String testBasename = "backupfilestest";
 
@@ -61,7 +62,7 @@ public class BackupFilesTest
 
   private static String suffix = "_BACKUPTEST-%n";
 
-  private static int digits = 8;
+  private static int digits = 6;
 
   private static int rollMax = 2;
 
@@ -107,28 +108,28 @@ public class BackupFilesTest
     }
 
     // check 10 backup files
-    HashMap correctindexmap = new HashMap();
-    correctindexmap.put(1, "backupfilestestTemp.fa_BACKUPTEST-00000001");
-    correctindexmap.put(2, "backupfilestestTemp.fa_BACKUPTEST-00000002");
-    correctindexmap.put(3, "backupfilestestTemp.fa_BACKUPTEST-00000003");
-    correctindexmap.put(4, "backupfilestestTemp.fa_BACKUPTEST-00000004");
-    correctindexmap.put(5, "backupfilestestTemp.fa_BACKUPTEST-00000005");
-    correctindexmap.put(6, "backupfilestestTemp.fa_BACKUPTEST-00000006");
-    correctindexmap.put(7, "backupfilestestTemp.fa_BACKUPTEST-00000007");
-    correctindexmap.put(8, "backupfilestestTemp.fa_BACKUPTEST-00000008");
-    correctindexmap.put(9, "backupfilestestTemp.fa_BACKUPTEST-00000009");
-    correctindexmap.put(10, "backupfilestestTemp.fa_BACKUPTEST-00000010");
-    HashMap wrongindexmap = new HashMap();
+    HashMap<Integer, String> correctindexmap = new HashMap<>();
+    correctindexmap.put(1, "backupfilestestTemp.fa_BACKUPTEST-000001");
+    correctindexmap.put(2, "backupfilestestTemp.fa_BACKUPTEST-000002");
+    correctindexmap.put(3, "backupfilestestTemp.fa_BACKUPTEST-000003");
+    correctindexmap.put(4, "backupfilestestTemp.fa_BACKUPTEST-000004");
+    correctindexmap.put(5, "backupfilestestTemp.fa_BACKUPTEST-000005");
+    correctindexmap.put(6, "backupfilestestTemp.fa_BACKUPTEST-000006");
+    correctindexmap.put(7, "backupfilestestTemp.fa_BACKUPTEST-000007");
+    correctindexmap.put(8, "backupfilestestTemp.fa_BACKUPTEST-000008");
+    correctindexmap.put(9, "backupfilestestTemp.fa_BACKUPTEST-000009");
+    correctindexmap.put(10, "backupfilestestTemp.fa_BACKUPTEST-000010");
+    HashMap<Integer, String> wrongindexmap = new HashMap<>();
     wrongindexmap.put(1, "backupfilestestTemp.fa_BACKUPTEST-1");
-    wrongindexmap.put(2, "backupfilestestTemp.fa_BACKUPTEST-00000002");
-    wrongindexmap.put(3, "backupfilestestTemp.fa_BACKUPTEST-00000003");
-    wrongindexmap.put(4, "backupfilestestTemp.fa_BACKUPTEST-00000004");
-    wrongindexmap.put(5, "backupfilestestTemp.fa_BACKUPTEST-00000005");
-    wrongindexmap.put(6, "backupfilestestTemp.fa_BACKUPTEST-00000006");
-    wrongindexmap.put(7, "backupfilestestTemp.fa_BACKUPTEST-00000007");
-    wrongindexmap.put(8, "backupfilestestTemp.fa_BACKUPTEST-00000008");
-    wrongindexmap.put(9, "backupfilestestTemp.fa_BACKUPTEST-00000009");
-    wrongindexmap.put(10, "backupfilestestTemp.fa_BACKUPTEST-00000010");
+    wrongindexmap.put(2, "backupfilestestTemp.fa_BACKUPTEST-000002");
+    wrongindexmap.put(3, "backupfilestestTemp.fa_BACKUPTEST-000003");
+    wrongindexmap.put(4, "backupfilestestTemp.fa_BACKUPTEST-000004");
+    wrongindexmap.put(5, "backupfilestestTemp.fa_BACKUPTEST-000005");
+    wrongindexmap.put(6, "backupfilestestTemp.fa_BACKUPTEST-000006");
+    wrongindexmap.put(7, "backupfilestestTemp.fa_BACKUPTEST-000007");
+    wrongindexmap.put(8, "backupfilestestTemp.fa_BACKUPTEST-000008");
+    wrongindexmap.put(9, "backupfilestestTemp.fa_BACKUPTEST-000009");
+    wrongindexmap.put(10, "backupfilestestTemp.fa_BACKUPTEST-000010");
     int[] indexes2 = { 3, 4, 5, 6, 7, 8, 9, 10 };
     int[] indexes3 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
     Assert.assertTrue(checkBackupFiles(correctindexmap));
@@ -158,9 +159,9 @@ public class BackupFilesTest
 
     // check there are "rollMax" backup files and they are all saved correctly
     // check 10 backup files
-    HashMap correctindexmap = new HashMap();
-    correctindexmap.put(9, "backupfilestestTemp.fa_BACKUPTEST-00000009");
-    correctindexmap.put(10, "backupfilestestTemp.fa_BACKUPTEST-00000010");
+    HashMap<Integer, String> correctindexmap = new HashMap<>();
+    correctindexmap.put(9, "backupfilestestTemp.fa_BACKUPTEST-000009");
+    correctindexmap.put(10, "backupfilestestTemp.fa_BACKUPTEST-000010");
     int[] indexes2 = { 10 };
     int[] indexes3 = { 8, 9, 10 };
     Assert.assertTrue(checkBackupFiles(correctindexmap));
@@ -189,9 +190,9 @@ public class BackupFilesTest
 
     // check there are "rollMax" backup files and they are all saved correctly
     // check 10 backup files
-    HashMap correctindexmap = new HashMap();
-    correctindexmap.put(1, "backupfilestestTemp.fa_BACKUPTEST-00000001");
-    correctindexmap.put(2, "backupfilestestTemp.fa_BACKUPTEST-00000002");
+    HashMap<Integer, String> correctindexmap = new HashMap<>();
+    correctindexmap.put(1, "backupfilestestTemp.fa_BACKUPTEST-000001");
+    correctindexmap.put(2, "backupfilestestTemp.fa_BACKUPTEST-000002");
     int[] indexes2 = { 1 };
     int[] indexes3 = { 1, 2, 3 };
     Assert.assertTrue(checkBackupFiles(correctindexmap));
@@ -209,6 +210,14 @@ public class BackupFilesTest
   {
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
 
+    BackupFilesPresetEntry bfpe = new BackupFilesPresetEntry(suffix, digits,
+            reverse, noMax, rollMax, false);
+
+    Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
+            Boolean.toString(enabled));
+    Cache.applicationProperties.setProperty(
+            BackupFilesPresetEntry.SAVEDCONFIG, bfpe.toString());
+    /*
     Cache.applicationProperties.setProperty(BackupFiles.ENABLED,
             Boolean.toString(enabled));
     Cache.applicationProperties.setProperty(BackupFiles.SUFFIX, suffix);
@@ -222,6 +231,7 @@ public class BackupFilesTest
             Integer.toString(rollMax));
     Cache.applicationProperties.setProperty(BackupFiles.CONFIRM_DELETE_OLD,
             "false");
+            */
   }
 
   private void save()
index 5be7968..cf7f58f 100644 (file)
@@ -89,8 +89,7 @@ public class IdentifyFileTest
   @DataProvider(name = "identifyFiles")
   public Object[][] IdentifyFileDP()
   {
-    return new Object[][] {
-        { "examples/example.json", FileFormat.Json },
+    return new Object[][] { { "examples/example.json", FileFormat.Json },
         { "examples/plantfdx.fa", FileFormat.Fasta },
         { "examples/dna_interleaved.phy", FileFormat.Phylip },
         { "examples/2GIS.pdb", FileFormat.PDB },
@@ -107,13 +106,12 @@ public class IdentifyFileTest
         { "examples/testdata/simpleGff3.gff", FileFormat.Features },
         { "examples/testdata/test.jvp", FileFormat.Jalview },
         { "examples/testdata/test.cif", FileFormat.MMCif },
-        {
-            "examples/testdata/cullpdb_pc25_res3.0_R0.3_d150729_chains9361.fasta.15316",
+        { "examples/testdata/cullpdb_pc25_res3.0_R0.3_d150729_chains9361.fasta.15316",
             FileFormat.Fasta },
         { "resources/scoreModel/pam250.scm", FileFormat.ScoreMatrix },
         { "resources/scoreModel/blosum80.scm", FileFormat.ScoreMatrix }
-    // { "examples/testdata/test.amsa", "AMSA" },
-    // { "examples/test.jnet", "JnetFile" },
+        // { "examples/testdata/test.amsa", "AMSA" },
+        // { "examples/test.jnet", "JnetFile" },
     };
   }
 
@@ -126,8 +124,8 @@ public class IdentifyFileTest
     // too few columns:
     assertFalse(id.looksLikeFeatureData("1 \t 2 \t 3 \t 4 \t 5"));
     // GFF format:
-    assertTrue(id
-            .looksLikeFeatureData("Seq1\tlocal\tHelix\t2456\t2462\tss"));
+    assertTrue(
+            id.looksLikeFeatureData("Seq1\tlocal\tHelix\t2456\t2462\tss"));
     // Jalview format:
     assertTrue(id.looksLikeFeatureData("Helix\tSeq1\t-1\t2456\t2462\tss"));
     // non-numeric start column:
index e86c8ad..ba4312a 100644 (file)
@@ -257,8 +257,8 @@ public class StockholmFileTest
           assertEquals("Threshold line not identical.",
                   aa_original[i].threshold, aa_new[i].threshold);
           // graphGroup may differ, but pattern should be the same
-          Integer o_ggrp = new Integer(aa_original[i].graphGroup + 2);
-          Integer n_ggrp = new Integer(aa_new[i].graphGroup + 2);
+          Integer o_ggrp = Integer.valueOf(aa_original[i].graphGroup + 2);
+          Integer n_ggrp = Integer.valueOf(aa_new[i].graphGroup + 2);
           BitSet orig_g = orig_groups.get(o_ggrp);
           BitSet new_g = new_groups.get(n_ggrp);
           if (orig_g == null)
index f180395..94bc5d9 100644 (file)
@@ -1,13 +1,10 @@
 #---JalviewX Properties File---
 #Fri Apr 25 09:54:25 BST 2014
 #
-BACKUPFILES_ROLL_MAX=2
-BACKUPFILES_REVERSE_ORDER=false
-BACKUPFILES_SUFFIX=_BACKUPFILESTESTTMP%n
-BACKUPFILES_CONFIRM_DELETE_OLD=false
-BACKUPFILES_NO_MAX=false
 BACKUPFILES_ENABLED=true
-BACKUPFILES_SUFFIX_DIGITS=8
+BACKUPFILES_PRESET=0
+BACKUPFILES_SAVED=_BACKUPFILESTESTTMP%n\t6\tfalse\tfalse\t2\tfalse
+BACKUPFILES_CUSTOM=_BACKUPFILESTESTTMP%n\t6\tfalse\tfalse\t2\tfalse
 SCREEN_Y=768
 SCREEN_X=936
 SHOW_WSDISCOVERY_ERRORS=true
index 0fdb941..8fcf8f5 100644 (file)
@@ -75,6 +75,7 @@ public class VCFLoaderTest
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     Cache.setProperty("VCF_FIELDS", ".*");
     Cache.setProperty("VEP_FIELDS", ".*");
+    Cache.setProperty("VCF_ASSEMBLY", "GRCh38=GRCh38");
     Cache.initLogger();
   }
 
index e9221e0..83cc683 100644 (file)
@@ -34,7 +34,10 @@ import jalview.api.FeatureColourI;
 import jalview.api.ViewStyleI;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.GeneLocus;
 import jalview.datamodel.HiddenSequences;
+import jalview.datamodel.Mapping;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.PDBEntry.Type;
 import jalview.datamodel.SequenceCollectionI;
@@ -68,6 +71,7 @@ import jalview.schemes.RNAHelicesColour;
 import jalview.schemes.StrandColourScheme;
 import jalview.schemes.TCoffeeColourScheme;
 import jalview.structure.StructureImportSettings;
+import jalview.util.MapList;
 import jalview.util.matcher.Condition;
 import jalview.viewmodel.AlignmentViewport;
 
@@ -1183,4 +1187,83 @@ public class Jalview2xmlTests extends Jalview2xmlBase
                     .getAlignViewport(),
             "Didn't restore correct view association for the PCA view");
   }
+
+  /**
+   * Test save and reload of DBRefEntry including GeneLocus in project
+   * 
+   * @throws Exception
+   */
+  @Test(groups = { "Functional" })
+  public void testStoreAndRecoverGeneLocus() throws Exception
+  {
+    Desktop.instance.closeAll_actionPerformed(null);
+    String seqData = ">P30419\nACDE\n>X1235\nGCCTGTGACGAA";
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(seqData,
+            DataSourceType.PASTE);
+    assertNotNull(af, "Didn't read in the example file correctly.");
+  
+    AlignmentViewPanel ap = Desktop.getAlignmentPanels(null)[0];
+    SequenceI pep = ap.getAlignment().getSequenceAt(0);
+    SequenceI cds = ap.getAlignment().getSequenceAt(1);
+
+    /*
+     * give 'protein' a dbref to self, a dbref with map to CDS,
+     * and a dbref with map to gene 'locus'
+     */
+    DBRefEntry dbref1 = new DBRefEntry("Uniprot", "1", "P30419", null);
+    pep.addDBRef(dbref1);
+    Mapping cdsmap = new Mapping(cds,
+            new MapList(new int[]
+            { 1, 4 }, new int[] { 1, 12 }, 1, 3));
+    DBRefEntry dbref2 = new DBRefEntry("EMBLCDS", "2", "X1235", cdsmap);
+    pep.addDBRef(dbref2);
+    Mapping locusmap = new Mapping(null,
+            new MapList(new int[]
+            { 1, 4 }, new int[] { 2674123, 2674135 }, 1, 3));
+    DBRefEntry dbref3 = new GeneLocus("human", "GRCh38", "5", locusmap);
+    pep.addDBRef(dbref3);
+
+    File tfile = File.createTempFile("testStoreAndRecoverGeneLocus",
+            ".jvp");
+    try
+    {
+      new Jalview2XML(false).saveState(tfile);
+    } catch (Throwable e)
+    {
+      Assert.fail("Didn't save the state", e);
+    }
+    Desktop.instance.closeAll_actionPerformed(null);
+  
+    new FileLoader().LoadFileWaitTillLoaded(tfile.getAbsolutePath(),
+            DataSourceType.FILE);
+    AlignmentViewPanel rap = Desktop.getAlignmentPanels(null)[0];
+    SequenceI rpep = rap.getAlignment().getSequenceAt(0);
+    assertEquals(rpep.getName(), "P30419");
+    DBRefEntry[] dbrefs = rpep.getDBRefs();
+    assertEquals(dbrefs.length, 3);
+    DBRefEntry dbRef = dbrefs[0];
+    assertFalse(dbRef instanceof GeneLocus);
+    assertNull(dbRef.getMap());
+    assertEquals(dbRef, dbref1);
+
+    /*
+     * restored dbrefs with mapping have a different 'map to'
+     * sequence but otherwise match the original dbrefs
+     */
+    dbRef = dbrefs[1];
+    assertFalse(dbRef instanceof GeneLocus);
+    assertTrue(dbRef.equalRef(dbref2));
+    assertNotNull(dbRef.getMap());
+    SequenceI rcds = rap.getAlignment().getSequenceAt(1);
+    assertSame(dbRef.getMap().getTo(), rcds);
+    // compare MapList but not map.to
+    assertEquals(dbRef.getMap().getMap(), dbref2.getMap().getMap());
+
+    /*
+     * GeneLocus map.to is null so can compare Mapping objects
+     */
+    dbRef = dbrefs[2];
+    assertTrue(dbRef instanceof GeneLocus);
+    assertEquals(dbRef, dbref3);
+  }
 }
index 52ca360..dae70f7 100644 (file)
@@ -173,6 +173,13 @@ public class FeatureColourTest
     assertEquals(Color.yellow, fc1.getColour());
     assertEquals(10f, fc1.getMin());
     assertEquals(20f, fc1.getMax());
+
+    /*
+     * modify original attribute label and check that copy doesn't change
+     */
+    fc.setAttributeName("MAF", "AF");
+    assertArrayEquals(new String[] { "AF" }, fc1.getAttributeName());
+
   }
 
   @Test(groups = { "Functional" })
index 5052894..d2c48f1 100644 (file)
  */
 package jalview.util;
 
+import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
 import jalview.gui.JvOptionPane;
 
 import java.awt.Button;
-import java.awt.Event;
 import java.awt.Toolkit;
+import java.awt.event.InputEvent;
 import java.awt.event.MouseEvent;
 
 import org.testng.annotations.BeforeClass;
@@ -72,11 +74,11 @@ public class PlatformTest
     assertFalse(Platform.isControlDown(new MouseEvent(b, 0, 0L, mods, 0, 0,
             0, 0, clickCount, isPopupTrigger, buttonNo), mac));
 
-    mods = Event.CTRL_MASK;
+    mods = InputEvent.CTRL_DOWN_MASK | InputEvent.BUTTON1_DOWN_MASK;
     assertFalse(Platform.isControlDown(new MouseEvent(b, 0, 0L, mods, 0, 0,
             0, 0, clickCount, isPopupTrigger, buttonNo), mac));
 
-    mods = Event.META_MASK;
+    mods = InputEvent.META_DOWN_MASK | InputEvent.BUTTON1_DOWN_MASK;
     assertTrue(Platform.isControlDown(new MouseEvent(b, 0, 0L, mods, 0, 0,
             0, 0, clickCount, isPopupTrigger, buttonNo), mac));
 
@@ -108,15 +110,35 @@ public class PlatformTest
     assertFalse(Platform.isControlDown(new MouseEvent(b, 0, 0L, mods, 0, 0,
             0, 0, clickCount, isPopupTrigger, buttonNo), mac));
 
-    mods = Event.CTRL_MASK;
+    mods = InputEvent.CTRL_DOWN_MASK;
     assertTrue(Platform.isControlDown(new MouseEvent(b, 0, 0L, mods, 0, 0,
             0, 0, clickCount, isPopupTrigger, buttonNo), mac));
 
-    mods = Event.CTRL_MASK | Event.SHIFT_MASK | Event.ALT_MASK;
+    mods = InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK
+            | InputEvent.ALT_DOWN_MASK;
     clickCount = 2;
     buttonNo = 2;
     isPopupTrigger = true;
     assertTrue(Platform.isControlDown(new MouseEvent(b, 0, 0L, mods, 0, 0,
             0, 0, clickCount, isPopupTrigger, buttonNo), mac));
   }
+
+  @Test(groups = "Functional")
+  public void testPathEquals()
+  {
+    assertTrue(Platform.pathEquals(null, null));
+    assertFalse(Platform.pathEquals(null, "apath"));
+    assertFalse(Platform.pathEquals("apath", null));
+    assertFalse(Platform.pathEquals("apath", "APATH"));
+    assertTrue(Platform.pathEquals("apath", "apath"));
+    assertTrue(Platform.pathEquals("apath/a/b", "apath\\a\\b"));
+  }
+
+  @Test(groups = "Functional")
+  public void testEscapeBackslashes()
+  {
+    assertNull(Platform.escapeBackslashes(null));
+    assertEquals(Platform.escapeBackslashes("hello world"), "hello world");
+    assertEquals(Platform.escapeBackslashes("hello\\world"), "hello\\\\world");
+  }
 }
index 6683d6c..0587352 100644 (file)
@@ -30,10 +30,14 @@ import jalview.ws.seqfetcher.ASequenceFetcher;
 import jalview.ws.seqfetcher.DbSourceProxy;
 
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Vector;
 
+import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
 
 public class SequenceFetcherTest
 {
@@ -45,6 +49,30 @@ public class SequenceFetcherTest
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
+  @Test(groups = "Functional")
+  public void testNoDuplicatesInFetchDbRefs()
+  {
+    Map<String, List<DbSourceProxy>> seen = new HashMap<>();
+    jalview.ws.SequenceFetcher sfetcher = new jalview.ws.SequenceFetcher();
+    String dupes = "";
+    for (String src : sfetcher.getOrderedSupportedSources())
+    {
+      List<DbSourceProxy> seenitem = seen.get(src);
+      if (seenitem != null)
+      {
+        dupes += (dupes.length() > 0 ? "," : "") + src;
+      }
+      else
+      {
+        seen.put(src, sfetcher.getSourceProxy(src));
+      }
+    }
+    if (dupes.length() > 0)
+    {
+      Assert.fail("Duplicate sources : " + dupes);
+    }
+  }
+
   /**
    * simple run method to test dbsources.
    * 
index 1279b31..dee5322 100644 (file)
@@ -282,9 +282,10 @@ public class HelpLinksChecker implements BufferedLineReader.LineCleaner
         else
         {
           internalHrefCount++;
+          String relFile = System.getProperty("os.name").indexOf("Win") > -1 ? href.replace("/", File.separator) : href;
           File hrefFile = href.equals("") ? htmlFile : new File(htmlFolder,
                   href);
-          if (hrefFile != htmlFile && !fileExists(hrefFile, href))
+          if (hrefFile != htmlFile && !fileExists(hrefFile, relFile))
           {
             badLink = true;
             invalidInternalHrefCount++;
diff --git a/utils/archive_building.sh b/utils/archive_building.sh
new file mode 100644 (file)
index 0000000..c29d824
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+J8HOME=/path/to/java8/jdk
+J11HOME=/path/to/java11/jdk
+OLDBUILDDIR=/path/to/old/build/root
+JALVIEWDIR=/path/to/recent/jalview
+LOCALARCHIVEDIR=/path/to/where/to/store/archive/locally
+WEBSITEDOCROOTMOUNT=/path/to/mounted/website/docroot
+
+
+cd OLDBUILDDIR
+mkdir tar
+cd tar
+wget http://www.jalview.org/source/jalview_2_08b.tar.gz  http://www.jalview.org/source/jalview_2_2_1.tar.gz  http://www.jalview.org/source/jalview_2_3_buildfix.tar.gz http://www.jalview.org/source/jalview_2_4_0b2.tar.gz http://www.jalview.org/source/jalview_2_5_1.tar.gz http://www.jalview.org/source/jalview_2_6_1.tar.gz http://www.jalview.org/source/jalview_2_7.tar.gz http://www.jalview.org/source/jalview_2_8_2b1.tar.gz http://www.jalview.org/source/jalview_2_9_0b1.tar.gz http://www.jalview.org/source/jalview_2_10_5.tar.gz
+cd -
+
+export JAVA_HOME=J8HOME
+export PATH=$JAVA_HOME/bin:$PATH
+
+for x in tar/jalview_*.tar.gz
+do
+       V=${x#*jalview_}
+       V=${V%.tar.gz}
+       echo $V
+       tar --one-top-level -xvf $x
+       cd jalview_$V/jalview
+       ant makedist -DJALVIEW_VERSION="$V"
+       cd -
+done
+
+export JAVA_HOME=J11HOME
+export PATH=$JAVA_HOME/bin:$PATH
+
+cd $JALVIEWDIR
+for x in $OLDBUILDDIR/jalview_*/jalview
+do
+       V=${x##*jalview_}
+       V=${V%/jalview}
+       echo $V
+       [ -e getdown/website ] && /bin/rm -r getdown/website
+       [ -e getdown/files ] && /bin/rm -r getdown/website
+       gradle getdown -PCHANNEL=ARCHIVE -PJALVIEW_VERSION="$V" -PJAVA_VERSION=1.8 -PARCHIVEDIR=$x -Pgetdown_rsync_dest=$LOCALARCHIVEDIR -PRUNRSYNC=true
+done
+
+
+cd $LOCALARCHIVEDIR
+rsync -avh --delete $LOCALARCHIVEDIR/archive/ $WEBSITEDOCROOTMOUNT/getdown/archive/
+
diff --git a/utils/de-multi-release-jar.sh b/utils/de-multi-release-jar.sh
new file mode 100755 (executable)
index 0000000..6a58c23
--- /dev/null
@@ -0,0 +1,80 @@
+#!/usr/bin/env bash
+
+CMD=$(basename $0)
+
+usage() {
+  echo "Usage: $CMD [-v N] /path/to/jarfile" >&2
+  echo "             -v N   build jar with alternative classes for version N of java (optional, defaults to base jar packages which is usually 8)" >&2
+}
+
+usagexit() {
+  usage
+  exit 1
+}
+
+error() {
+  echo $1 >&2
+  usagexit
+}
+
+VERSION=""
+while getopts v: opt
+do
+  case "${opt}"
+    in
+    v) VERSION=${OPTARG};;
+  esac
+done
+shift $((OPTIND-1))
+
+JARFILE=$1
+[ -z $JARFILE ] && usagexit
+[ -f $JARFILE ] || error "No file $JARFILE"
+[ -r $JARFILE ] || error "$JARFILE not readable"
+
+EXT=.jar
+SUFFIX=-SINGLE_RELEASE
+FILENAME=$(basename $JARFILE)
+BASE=$(basename -s $EXT $JARFILE)
+DIR=$(dirname $JARFILE)
+
+# set absolute path to $JARFILE if not specified
+[ x${DIR#/} = x$DIR ] && DIR=$(cd "$DIR" && pwd)
+
+TMPDIR=/tmp/$USER-$CMD-$BASE-$$
+
+[ x$FILENAME = x$BASE ] && error "Should be $EXT file"
+
+mkdir -p $TMPDIR || error "Could not create tmp dir $TMPDIR"
+cd $TMPDIR
+jar -xvf $JARFILE > /dev/null
+VDIR=$TMPDIR/META-INF/versions
+
+[ -d $VDIR ] || error "$JARFILE doesn't look like a multi-release jar file"
+
+if [ -z $VERSION ]; then
+  # no version set... nothing to copy
+  echo ""
+elif [ -d $VDIR/$VERSION ]; then
+  # this version has alternative classes for the version asked for, copy them into the base jar
+  tar -cf - -C $VDIR/$VERSION . | tar -xf - -C $TMPDIR
+else
+  echo "No specific classes for version $VERSION" >&2
+  echo "Available alternative versions are" >&2
+  cd $VDIR
+  ls >&2
+fi
+
+# remove the alternative versions
+/bin/rm -r $VDIR
+
+# alter the manifest. note sed on macos is a bit weird
+if [ `uname -s` = "Darwin" ]; then
+  sed -E -i '' 's/^([Mm]ulti-[Rr]elease):).*/\1 false/' $TMPDIR/META-INF/MANIFEST.MF
+else
+  sed -E -i 's/^([Mm]ulti-[Rr]elease):).*/\1 false/' $TMPDIR/META-INF/MANIFEST.MF
+fi
+
+jar -cvf $DIR/${BASE}${SUFFIX}${EXT} -C $TMPDIR . > /dev/null
+rm -rf $TMPDIR
+
diff --git a/utils/deprecation_auto_fixes.sh b/utils/deprecation_auto_fixes.sh
new file mode 100644 (file)
index 0000000..27a6319
--- /dev/null
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+TYPES="Boolean|Character|Double|Float|Long|Integer" find src test -type f -name "*.java" -exec perl -p -i -e 's/\bnew\s+(java\.lang\.)?($ENV{TYPES})\(/${1}${2}.valueOf(/;' {} +
+
+find src test -type f -name "*.java" -exec perl -p -i -e 's/(InputEvent|KeyEvent)\s*\.\s*([A-Z0-9]+)_MASK\b/${1}.${2}_DOWN_MASK/g;' {} +
+
+find src test -type f -name "*.java" -exec perl -p -i -e 's/\b(e|evt)\.getModifiers\b/${1}.getModifiersEx/g;' {} +
+
+find src test -type f -name "*.java" -exec perl -p -i -e 's/\.getMenuShortcutKeyMask\b/\.getMenuShortcutKeyMaskEx/g;' {} +
+
+#
+find src test -type f -name "*.java" -exec perl -p -i -e 'if ( s/^\s*import\s+java\.awt\.Event\s*;\s*$/import java.awt.event.InputEvent;/ ) { $event = 1 }; if ($event == 1) { s/\b(java\.awt\.)?Event\s*\.\s*([A-Z0-9]+)_MASK\b/InputEvent.${2}_DOWN_MASK/g; s/\b(java\.awt\.)?Event\s*\.\s*(MOUSE_MOVE)/MouseEvent.MOUSE_MOVED/g }' {} +
diff --git a/utils/dev_macos_install.sh b/utils/dev_macos_install.sh
new file mode 100755 (executable)
index 0000000..5eadf0d
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+
+# perform a dev build and install on local macOS machine
+INSTALLERVOL="Jalview Installer"
+APP=Jalview.app
+
+APPLICATIONS=/Applications
+CHANNEL=NOCHANNEL
+DMG=build/install4j/1.8/Jalview-OFFLINE_macos-app_DEVELOPMENT-j8.dmg
+
+if [ $1 != "nogradle" ]; then
+  gradle installers -Pgetdown_channel_name=NOCHANNEL -Pinstall4jMediaTypes=macosArchive -Pgetdown_local=true -Pdev=true
+else
+  echo "Not running gradle installers"
+fi
+
+if [ $? = 0 ]; then
+  umount "/Volumes/$INSTALLERVOL"
+  if [ -e "$DMG" ]; then
+    open $DMG
+  else
+    echo "No DMG file '$DMG'" 1>&2
+    exit 1
+  fi
+  echo "Mounting '$DMG' at /Volumes"
+  N=0
+  while [ \! -e "/Volumes/$INSTALLERVOL/$APP" ]; do
+    if [ $(( N%1000 )) = 0 ]; then
+      echo -n "."
+    fi
+    N=$(( N+1 ))
+  done
+  echo ""
+fi
+if [ -e "/Volumes/$INSTALLERVOL/$APP" ]; then
+  /bin/rm -r "$APPLICATIONS/$APP"
+  rsync -avh "/Volumes/$INSTALLERVOL/$APP" "$APPLICATIONS/"
+  umount "/Volumes/$INSTALLERVOL"
+fi
index 8496f8b..8e09423 100644 (file)
@@ -139,7 +139,7 @@ public class getJavaVersion
     if (versions == null)
     {
       versions = new Hashtable();
-      versions.put("45.3", "1.0");
+      versions.put("45.3", "1.0.2");
       versions.put("45.3", "1.1");
       versions.put("46.0", "1.2");
       versions.put("47.0", "1.3");
@@ -148,6 +148,9 @@ public class getJavaVersion
       versions.put("50.0", "1.6");
       versions.put("51.0", "1.7");
       versions.put("52.0", "1.8");
+      versions.put("53.0", "9");
+      versions.put("54.0", "10");
+      versions.put("55.0", "11");
 
     }
     String version = (String) versions.get(major + "." + minor);
diff --git a/utils/getdown/jalview_logo_background_fade-640x480.xcf b/utils/getdown/jalview_logo_background_fade-640x480.xcf
new file mode 100644 (file)
index 0000000..a194fee
Binary files /dev/null and b/utils/getdown/jalview_logo_background_fade-640x480.xcf differ
diff --git a/utils/getdown/jalview_logo_background_getdown-640x480.png b/utils/getdown/jalview_logo_background_getdown-640x480.png
new file mode 100644 (file)
index 0000000..39f9cfd
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown-640x480.png differ
diff --git a/utils/getdown/jalview_logo_background_getdown-progress-TEST2.png b/utils/getdown/jalview_logo_background_getdown-progress-TEST2.png
new file mode 100644 (file)
index 0000000..1a9bf34
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown-progress-TEST2.png differ
diff --git a/utils/getdown/jalview_logo_background_getdown-progress.png b/utils/getdown/jalview_logo_background_getdown-progress.png
new file mode 100644 (file)
index 0000000..71066e8
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown-progress.png differ
diff --git a/utils/getdown/jalview_logo_background_getdown-progress1.png b/utils/getdown/jalview_logo_background_getdown-progress1.png
new file mode 100644 (file)
index 0000000..804063a
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown-progress1.png differ
diff --git a/utils/getdown/jalview_logo_background_getdown-progress2.png b/utils/getdown/jalview_logo_background_getdown-progress2.png
new file mode 100644 (file)
index 0000000..fb87750
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown-progress2.png differ
diff --git a/utils/getdown/jalview_logo_background_getdown-progress3-0.png b/utils/getdown/jalview_logo_background_getdown-progress3-0.png
new file mode 100644 (file)
index 0000000..cb1475c
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown-progress3-0.png differ
diff --git a/utils/getdown/jalview_logo_background_getdown-progress3-1.png b/utils/getdown/jalview_logo_background_getdown-progress3-1.png
new file mode 100644 (file)
index 0000000..71066e8
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown-progress3-1.png differ
diff --git a/utils/getdown/jalview_logo_background_getdown-progress3.xcf b/utils/getdown/jalview_logo_background_getdown-progress3.xcf
new file mode 100644 (file)
index 0000000..70b4f01
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown-progress3.xcf differ
diff --git a/utils/getdown/jalview_logo_background_getdown-progress4.png b/utils/getdown/jalview_logo_background_getdown-progress4.png
new file mode 100644 (file)
index 0000000..bc7e125
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown-progress4.png differ
diff --git a/utils/getdown/jalview_logo_background_getdown_error-640x480.png b/utils/getdown/jalview_logo_background_getdown_error-640x480.png
new file mode 100644 (file)
index 0000000..d331daf
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown_error-640x480.png differ
diff --git a/utils/getdown/jalview_logo_background_getdown_instant-640x480.png b/utils/getdown/jalview_logo_background_getdown_instant-640x480.png
new file mode 100644 (file)
index 0000000..21e77cf
Binary files /dev/null and b/utils/getdown/jalview_logo_background_getdown_instant-640x480.png differ
diff --git a/utils/install4j/DS_Store b/utils/install4j/DS_Store
new file mode 100644 (file)
index 0000000..21fc79e
Binary files /dev/null and b/utils/install4j/DS_Store differ
diff --git a/utils/install4j/DS_Store_1 b/utils/install4j/DS_Store_1
new file mode 100644 (file)
index 0000000..374da46
Binary files /dev/null and b/utils/install4j/DS_Store_1 differ
diff --git a/utils/install4j/DS_Store_2 b/utils/install4j/DS_Store_2
new file mode 100644 (file)
index 0000000..2eafdbc
Binary files /dev/null and b/utils/install4j/DS_Store_2 differ
diff --git a/utils/install4j/DS_Store_3 b/utils/install4j/DS_Store_3
new file mode 100644 (file)
index 0000000..21fc79e
Binary files /dev/null and b/utils/install4j/DS_Store_3 differ
diff --git a/utils/install4j/Info_plist_file_associations.xml b/utils/install4j/Info_plist_file_associations.xml
new file mode 100644 (file)
index 0000000..bcf4783
--- /dev/null
@@ -0,0 +1,39 @@
+<key>CFBundleDocumentTypes</key>
+<array>
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>jvp</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Jalview Project File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-jalview-project</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>jvl</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Jalview Version Locator</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-Version-Locator.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-jalview-version-locator</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+</array>
diff --git a/utils/install4j/Jalview-File.icns b/utils/install4j/Jalview-File.icns
new file mode 100644 (file)
index 0000000..f8a5ec2
Binary files /dev/null and b/utils/install4j/Jalview-File.icns differ
diff --git a/utils/install4j/Jalview-File.ico b/utils/install4j/Jalview-File.ico
new file mode 100644 (file)
index 0000000..110964a
Binary files /dev/null and b/utils/install4j/Jalview-File.ico differ
diff --git a/utils/install4j/Jalview-Version-Locator.icns b/utils/install4j/Jalview-Version-Locator.icns
new file mode 100644 (file)
index 0000000..506b5a1
Binary files /dev/null and b/utils/install4j/Jalview-Version-Locator.icns differ
diff --git a/utils/install4j/Jalview-Version-Locator.ico b/utils/install4j/Jalview-Version-Locator.ico
new file mode 100644 (file)
index 0000000..4a2545b
Binary files /dev/null and b/utils/install4j/Jalview-Version-Locator.ico differ
diff --git a/utils/install4j/Jalview-Version-Locator.png b/utils/install4j/Jalview-Version-Locator.png
new file mode 100644 (file)
index 0000000..cb2ce97
Binary files /dev/null and b/utils/install4j/Jalview-Version-Locator.png differ
diff --git a/utils/install4j/JalviewVersionLocator.png b/utils/install4j/JalviewVersionLocator.png
new file mode 100644 (file)
index 0000000..9441080
Binary files /dev/null and b/utils/install4j/JalviewVersionLocator.png differ
diff --git a/utils/install4j/Uninstall Old Jalview.app/Contents/Info.plist b/utils/install4j/Uninstall Old Jalview.app/Contents/Info.plist
new file mode 100644 (file)
index 0000000..649770d
--- /dev/null
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleAllowMixedLocalizations</key>
+       <true/>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>applet</string>
+       <key>CFBundleIconFile</key>
+       <string>applet</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.apple.ScriptEditor.id.Uninstall-Old-Jalview</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>Uninstall Old Jalview</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleShortVersionString</key>
+       <string>1.0</string>
+       <key>CFBundleSignature</key>
+       <string>aplt</string>
+       <key>LSMinimumSystemVersionByArchitecture</key>
+       <dict>
+               <key>x86_64</key>
+               <string>10.6</string>
+       </dict>
+       <key>LSRequiresCarbon</key>
+       <true/>
+       <key>NSAppleEventsUsageDescription</key>
+       <string>This script needs to control other applications to run.</string>
+       <key>NSAppleMusicUsageDescription</key>
+       <string>This script needs access to your music to run.</string>
+       <key>NSCalendarsUsageDescription</key>
+       <string>This script needs access to your calendars to run.</string>
+       <key>NSCameraUsageDescription</key>
+       <string>This script needs access to your camera to run.</string>
+       <key>NSContactsUsageDescription</key>
+       <string>This script needs access to your contacts to run.</string>
+       <key>NSHomeKitUsageDescription</key>
+       <string>This script needs access to your HomeKit Home to run.</string>
+       <key>NSMicrophoneUsageDescription</key>
+       <string>This script needs access to your microphone to run.</string>
+       <key>NSPhotoLibraryUsageDescription</key>
+       <string>This script needs access to your photos to run.</string>
+       <key>NSRemindersUsageDescription</key>
+       <string>This script needs access to your reminders to run.</string>
+       <key>NSSiriUsageDescription</key>
+       <string>This script needs access to Siri to run.</string>
+       <key>NSSystemAdministrationUsageDescription</key>
+       <string>This script needs access to administer this system to run.</string>
+       <key>WindowState</key>
+       <dict>
+               <key>bundleDividerCollapsed</key>
+               <true/>
+               <key>bundlePositionOfDivider</key>
+               <real>0.0</real>
+               <key>dividerCollapsed</key>
+               <true/>
+               <key>eventLogLevel</key>
+               <integer>2</integer>
+               <key>name</key>
+               <string>ScriptWindowState</string>
+               <key>positionOfDivider</key>
+               <real>421</real>
+               <key>savedFrame</key>
+               <string>272 342 1754 910 0 0 3360 1867 </string>
+               <key>selectedTab</key>
+               <string>result</string>
+       </dict>
+</dict>
+</plist>
diff --git a/utils/install4j/Uninstall Old Jalview.app/Contents/MacOS/applet b/utils/install4j/Uninstall Old Jalview.app/Contents/MacOS/applet
new file mode 100755 (executable)
index 0000000..191894d
Binary files /dev/null and b/utils/install4j/Uninstall Old Jalview.app/Contents/MacOS/applet differ
diff --git a/utils/install4j/Uninstall Old Jalview.app/Contents/PkgInfo b/utils/install4j/Uninstall Old Jalview.app/Contents/PkgInfo
new file mode 100644 (file)
index 0000000..3253614
--- /dev/null
@@ -0,0 +1 @@
+APPLaplt
\ No newline at end of file
diff --git a/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/Scripts/main.scpt b/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/Scripts/main.scpt
new file mode 100644 (file)
index 0000000..3147d89
Binary files /dev/null and b/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/Scripts/main.scpt differ
diff --git a/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.icns b/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.icns
new file mode 100644 (file)
index 0000000..67a2cbd
Binary files /dev/null and b/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.icns differ
diff --git a/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.rsrc b/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.rsrc
new file mode 100644 (file)
index 0000000..8e05928
Binary files /dev/null and b/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.rsrc differ
diff --git a/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/description.rtfd/TXT.rtf b/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/description.rtfd/TXT.rtf
new file mode 100644 (file)
index 0000000..5f23fc5
--- /dev/null
@@ -0,0 +1,5 @@
+{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf500
+{\fonttbl}
+{\colortbl;\red255\green255\blue255;}
+{\*\expandedcolortbl;;}
+}
\ No newline at end of file
diff --git a/utils/install4j/Uninstall Old Jalview.app/Contents/_CodeSignature/CodeResources b/utils/install4j/Uninstall Old Jalview.app/Contents/_CodeSignature/CodeResources
new file mode 100644 (file)
index 0000000..04a1485
--- /dev/null
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>files</key>
+       <dict>
+               <key>Resources/Scripts/main.scpt</key>
+               <data>
+               KEuZnZPFR5wgwG/qEhXMd5yBoi4=
+               </data>
+               <key>Resources/applet.icns</key>
+               <data>
+               vIRz6m6+ZxDNfi4iTsXj5rdcZUA=
+               </data>
+               <key>Resources/applet.rsrc</key>
+               <data>
+               oLbJze+WI6mK9fT14HFV6EwFoEI=
+               </data>
+               <key>Resources/description.rtfd/TXT.rtf</key>
+               <data>
+               JdCHmFsejhMRQNi2CzUAg7xM/6Q=
+               </data>
+       </dict>
+       <key>files2</key>
+       <dict>
+               <key>Resources/Scripts/main.scpt</key>
+               <dict>
+                       <key>hash</key>
+                       <data>
+                       KEuZnZPFR5wgwG/qEhXMd5yBoi4=
+                       </data>
+                       <key>hash2</key>
+                       <data>
+                       Y+OMztx129elZ3oX0uhaiMMNU87xhkEPVzSuhF528t0=
+                       </data>
+               </dict>
+               <key>Resources/applet.icns</key>
+               <dict>
+                       <key>hash</key>
+                       <data>
+                       vIRz6m6+ZxDNfi4iTsXj5rdcZUA=
+                       </data>
+                       <key>hash2</key>
+                       <data>
+                       D7gig1wJlOzR/Iy+y6TESLN0j/cIpjThUyO1pj5fZEc=
+                       </data>
+               </dict>
+               <key>Resources/applet.rsrc</key>
+               <dict>
+                       <key>hash</key>
+                       <data>
+                       oLbJze+WI6mK9fT14HFV6EwFoEI=
+                       </data>
+                       <key>hash2</key>
+                       <data>
+                       6bi/D/GzKmLhXbbC8+OLEX9+44Au0XOyGRd+kfw6uzA=
+                       </data>
+               </dict>
+               <key>Resources/description.rtfd/TXT.rtf</key>
+               <dict>
+                       <key>hash</key>
+                       <data>
+                       JdCHmFsejhMRQNi2CzUAg7xM/6Q=
+                       </data>
+                       <key>hash2</key>
+                       <data>
+                       e8RCmynIiyJGwHTnWQowZeGP0OUnjjA6SQTvIzP7Hxs=
+                       </data>
+               </dict>
+       </dict>
+       <key>rules</key>
+       <dict>
+               <key>^Resources/</key>
+               <true/>
+               <key>^Resources/.*\.lproj/</key>
+               <dict>
+                       <key>optional</key>
+                       <true/>
+                       <key>weight</key>
+                       <real>1000</real>
+               </dict>
+               <key>^Resources/.*\.lproj/locversion.plist$</key>
+               <dict>
+                       <key>omit</key>
+                       <true/>
+                       <key>weight</key>
+                       <real>1100</real>
+               </dict>
+               <key>^Resources/Base\.lproj/</key>
+               <dict>
+                       <key>weight</key>
+                       <real>1010</real>
+               </dict>
+               <key>^version.plist$</key>
+               <true/>
+       </dict>
+       <key>rules2</key>
+       <dict>
+               <key>.*\.dSYM($|/)</key>
+               <dict>
+                       <key>weight</key>
+                       <real>11</real>
+               </dict>
+               <key>^(.*/)?\.DS_Store$</key>
+               <dict>
+                       <key>omit</key>
+                       <true/>
+                       <key>weight</key>
+                       <real>2000</real>
+               </dict>
+               <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
+               <dict>
+                       <key>nested</key>
+                       <true/>
+                       <key>weight</key>
+                       <real>10</real>
+               </dict>
+               <key>^.*</key>
+               <true/>
+               <key>^Info\.plist$</key>
+               <dict>
+                       <key>omit</key>
+                       <true/>
+                       <key>weight</key>
+                       <real>20</real>
+               </dict>
+               <key>^PkgInfo$</key>
+               <dict>
+                       <key>omit</key>
+                       <true/>
+                       <key>weight</key>
+                       <real>20</real>
+               </dict>
+               <key>^Resources/</key>
+               <dict>
+                       <key>weight</key>
+                       <real>20</real>
+               </dict>
+               <key>^Resources/.*\.lproj/</key>
+               <dict>
+                       <key>optional</key>
+                       <true/>
+                       <key>weight</key>
+                       <real>1000</real>
+               </dict>
+               <key>^Resources/.*\.lproj/locversion.plist$</key>
+               <dict>
+                       <key>omit</key>
+                       <true/>
+                       <key>weight</key>
+                       <real>1100</real>
+               </dict>
+               <key>^Resources/Base\.lproj/</key>
+               <dict>
+                       <key>weight</key>
+                       <real>1010</real>
+               </dict>
+               <key>^[^/]+$</key>
+               <dict>
+                       <key>nested</key>
+                       <true/>
+                       <key>weight</key>
+                       <real>10</real>
+               </dict>
+               <key>^embedded\.provisionprofile$</key>
+               <dict>
+                       <key>weight</key>
+                       <real>20</real>
+               </dict>
+               <key>^version\.plist$</key>
+               <dict>
+                       <key>weight</key>
+                       <real>20</real>
+               </dict>
+       </dict>
+</dict>
+</plist>
diff --git a/utils/install4j/Uninstall Old Jalview.scpt b/utils/install4j/Uninstall Old Jalview.scpt
new file mode 100644 (file)
index 0000000..3ab3516
Binary files /dev/null and b/utils/install4j/Uninstall Old Jalview.scpt differ
diff --git a/utils/install4j/auto_file_associations.pl b/utils/install4j/auto_file_associations.pl
new file mode 100755 (executable)
index 0000000..e59044f
--- /dev/null
@@ -0,0 +1,160 @@
+#!/usr/bin/env perl
+
+use strict;
+
+my $fileformats = $ARGV[0];
+$fileformats = "../../src/jalview/io/FileFormat.java" unless $fileformats;
+
+# default mimetype will be text/x-$shortname
+# TODO: find an actual extension for mat, see JAL-Xxxxx for outstanding issues too
+# TODO: look up standard mime type used for BLASTfmt matrices, etc
+my $mimetypes = {
+  rnaml => "application/rnaml+xml",
+  biojson => "application/x-jalview-biojson+json",
+  jnet => "application/x-jalview-jnet+text",
+  features => "application/x-jalview-features+text",
+  scorematrix => "application/x-jalview-scorematrix+text",
+  pdb => "chemical/x-pdb",
+  mmcif => "chemical/x-cif",
+  mmcif2 => "chemical/x-mcif",
+  jalview => "application/x-jalview+xml+zip",
+  jvl => "application/x-jalview-jvl+text",
+  annotations => "application/x-jalview-annotations+text",
+};
+
+my @dontaddshortname = qw(features json);
+my @dontaddextension = qw(html xml json jar mfa fastq);
+my $add_associations = {
+  biojson => {shortname=>"biojson",name=>"BioJSON",extensions=>["biojson"]},
+  gff2 => {shortname=>"gff2",name=>"Generic Features Format v2",extensions=>["gff2"]},
+  gff3 => {shortname=>"gff3",name=>"Generic Features Format v3",extensions=>["gff3"]},
+  features => {shortname=>"features",name=>"Jalview Features",extensions=>["features","jvfeatures"]},
+  annotations => {shortname=>"annotations",name=>"Jalview Annotations",extensions=>["annotations","jvannotations"]},
+  mmcif2 => {shortname=>"mmcif2",name=>"mmCIF",extensions=>["mcif","mmcif"]},
+  jvl => {shortname=>"jvl",name=>"Jalview Version Locator",extensions=>["jvl"],iconfile=>"Jalview-Version-Locator"},
+  jnet => {shortname=>"jnet",name=>"JnetFile",extensions=>["concise","jnet"]},
+  scorematrix => {shortname=>"scorematrix",name=>"Substitution Matrix",extensions=>["mat"]},
+};
+my $add_extensions = {
+  blc => ["blc"],
+};
+my @put_first = qw(jalview jvl);
+
+my $mactemplatefile = "file_associations_template-Info_plist.xml";
+my $i4jtemplatefile = "file_associations_template-install4j.xml";
+my $mactemplate;
+my $i4jtemplate;
+open(MT,"<$mactemplatefile") or dir("Could not open '$mactemplatefile' for reading");
+while(<MT>){
+  $mactemplate .= $_;
+}
+close(MT);
+open(IT,"<$i4jtemplatefile") or dir("Could not open '$i4jtemplatefile' for reading");
+while(<IT>){
+  $i4jtemplate .= $_;
+}
+close(IT);
+my $macauto;
+my $i4jauto;
+
+my $macautofile = $mactemplatefile;
+$macautofile =~ s/template/auto$1/;
+my $i4jautofile = $i4jtemplatefile;
+$i4jautofile =~ s/template/auto$1/;
+
+open(MA,">$macautofile") or die ("Could not open '$macautofile' for writing");
+print MA "<key>CFBundleDocumentTypes</key>\n<array>\n\n";
+open(IA,">$i4jautofile") or die ("Could not open '$i4jautofile' for writing");
+
+open(IN, "<$fileformats") or die ("Could not open '$fileformats' for reading");
+my $id = 10000;
+my $file_associations = {};
+while(my $line = <IN>) {
+  $line =~ s/\s+/ /g;
+  $line =~ s/(^ | $)//g;
+  if ($line =~ m/^(\w+) ?\( ?"([^"]*)" ?, ?"([^"]*)" ?, ?(true|false) ?, ?(true|false) ?\)$/i) {
+    my $shortname = lc($1);
+    next if (grep($_ eq $shortname, @dontaddshortname));
+    my $name = $2;
+    my $extensions = $3;
+    $extensions =~ s/\s+//g;
+    my @possextensions = split(m/,/,$extensions);
+    my @extensions;
+    my $addext = $add_extensions->{$shortname};
+    if (ref($addext) eq "ARRAY") {
+      push(@possextensions, @$addext);
+    }
+    for my $possext (@possextensions) {
+      next if grep($_ eq $possext, @extensions);
+      next if grep($_ eq $possext, @dontaddextension);
+      push(@extensions,$possext);
+    }
+    next unless scalar(@extensions);
+    $file_associations->{$shortname} = {
+      shortname => $shortname,
+      name => $name,
+      extensions => \@extensions
+    };
+    warn("Adding file association for $shortname\n");
+  }
+}
+close(IN);
+
+my %all_associations = (%$file_associations, %$add_associations);
+
+for my $shortname (@put_first, sort keys %all_associations) {
+  my $a = $all_associations{$shortname};
+  if (ref($a) ne "HASH") {
+    next;
+  }
+
+  my $name = $a->{name};
+  my $extensions = $a->{extensions};
+  my $mimetype = $mimetypes->{$shortname};
+  $mimetype = "text/x-$shortname" unless $mimetype;
+
+  my $iconfile = $a->{iconfile};
+  $iconfile = "Jalview-File" unless $iconfile;
+
+  my @extensions = @$extensions;
+  #warn("LINE: $line\nFound extensions '".join("', '", @extensions)."' for $name Files ($shortname)'n");
+
+  my $macentry = $mactemplate;
+  my $i4jentry = $i4jtemplate;
+
+  $macentry =~ s/\$\$NAME\$\$/$name/g;
+  $macentry =~ s/\$\$SHORTNAME\$\$/$shortname/g;
+  $macentry =~ s/\$\$MIMETYPE\$\$/$mimetype/g;
+  $macentry =~ s/\$\$ICONFILE\$\$/$iconfile/g;
+
+  $i4jentry =~ s/\$\$NAME\$\$/$name/g;
+  $i4jentry =~ s/\$\$SHORTNAME\$\$/$shortname/g;
+  $i4jentry =~ s/\$\$MIMETYPE\$\$/$mimetype/g;
+  $i4jentry =~ s/\$\$ICONFILE\$\$/$iconfile/g;
+
+  while ($macentry =~ m/\$\$([^\$]*)EXTENSIONS([^\$]*)\$\$/) {
+    my $pre = $1;
+    my $post = $2;
+    my $macextensions;
+    for my $ext (@extensions) {
+      $macextensions .= $pre.$ext.$post;
+    }
+    $macentry =~ s/\$\$${pre}EXTENSIONS${post}\$\$/$macextensions/g;
+  }
+  print MA $macentry;
+
+  for my $ext (@extensions) {
+    my $i4jextentry = $i4jentry;
+    $i4jextentry =~ s/\$\$EXTENSION\$\$/$ext/g;
+    $i4jextentry =~ s/\$\$ID\$\$/$id/g;
+    $id++;
+
+    print IA $i4jextentry;
+  }
+
+  delete $all_associations{$shortname};
+}
+
+close(IA);
+print MA "</array>\n";
+close(MA);
diff --git a/utils/install4j/bs_install4j_template.install4j b/utils/install4j/bs_install4j_template.install4j
new file mode 100644 (file)
index 0000000..b76d779
--- /dev/null
@@ -0,0 +1,1862 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<install4j version="7.0.9" transformSequenceNumber="7">
+  <directoryPresets config="../../../../../buildtools/jre/openjdk-java_vm/macos-jdk8u202-b08-jre" />
+  <application name="Jalview" distributionSourceDir="" applicationId="6595-2347-1923-0725" mediaDir="../../build/install4j/$$JAVA_VERSION$$" mediaFilePattern="${compiler:sys.shortName}_${compiler:sys.platform}_${compiler:sys.version}" compression="6" lzmaCompression="true" pack200Compression="false" excludeSignedFromPacking="true" commonExternalFiles="false" createMd5Sums="true" shrinkRuntime="true" shortName="Jalview" publisher="University of Dundee" publisherWeb="http://www.jalview.org/" version="$$VERSION$$" allPathsRelative="true" backupOnSave="false" autoSave="false" convertDotsToUnderscores="true" macSignature="????" macVolumeId="5aac4968c304f65" javaMinVersion="9999999999" javaMaxVersion="" allowBetaVM="true" jdkMode="jdk" jdkName="JDK 11.0">
+    <languages skipLanguageSelection="false" languageSelectionInPrincipalLanguage="false">
+      <principalLanguage id="en" customLocalizationFile="" />
+      <additionalLanguages />
+    </languages>
+    <searchSequence>
+      <directory location="jre" />
+    </searchSequence>
+    <variables>
+      <variable name="OSX_KEYSTORE" value="" description="" category="" />
+      <variable name="JSIGN_SH" value="" description="" category="" />
+    </variables>
+    <mergedProjects />
+    <codeSigning macEnabled="true" macPkcs12File="${compiler:OSX_KEYSTORE}" windowsEnabled="false" windowsKeySource="pkcs12" windowsPvkFile="" windowsSpcFile="" windowsPkcs12File="" windowsPkcs11Library="" windowsPkcs11Slot="">
+      <windowsKeystoreIdentifier issuer="" serial="" subject="" />
+      <windowsPkcs11Identifier issuer="" serial="" subject="" />
+    </codeSigning>
+  </application>
+  <files keepModificationTimes="false" missingFilesStrategy="warn" globalExcludeSuffixes="" defaultOverwriteMode="4" defaultUninstallMode="2" launcherOverwriteMode="3" defaultFileMode="644" defaultDirMode="755">
+    <filesets>
+      <fileset name="Full file set" id="734" customizedId="" />
+      <fileset name="Mac OS X JRE" id="880" customizedId="" />
+      <fileset name="Windows JRE" id="882" customizedId="" />
+    </filesets>
+    <roots>
+      <root id="735" fileset="734" location="" />
+      <root id="881" fileset="880" location="" />
+      <root id="883" fileset="882" location="" />
+    </roots>
+    <mountPoints>
+      <mountPoint id="454" root="" location="" mode="755" />
+      <mountPoint id="736" root="735" location="" mode="755" />
+      <mountPoint id="884" root="881" location="" mode="755" />
+      <mountPoint id="885" root="883" location="" mode="755" />
+    </mountPoints>
+    <entries>
+      <dirEntry mountPoint="454" file="../../getdown/files/$$JAVA_VERSION$$" overwriteMode="4" shared="false" fileMode="644" uninstallMode="2" overrideFileMode="false" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="direct" subDirectory="files" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="736" file="../../getdown/website/$$JAVA_VERSION$$" overwriteMode="4" shared="false" fileMode="644" uninstallMode="2" overrideFileMode="false" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="direct" subDirectory="files" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="884" file="$$MACOS_JAVA_VM_DIR$$" overwriteMode="4" shared="false" fileMode="755" uninstallMode="0" overrideFileMode="true" overrideOverwriteMode="false" overrideUninstallMode="true" entryMode="subdir" subDirectory="jre" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="885" file="$$WINDOWS_JAVA_VM_DIR$$" overwriteMode="4" shared="false" fileMode="755" uninstallMode="0" overrideFileMode="true" overrideOverwriteMode="false" overrideUninstallMode="true" entryMode="subdir" subDirectory="jre" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+    </entries>
+    <components>
+      <component name="jalview_getdown" id="1031" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_734" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+      <component name="macos_java_vm" id="1155" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_880" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+      <component name="windows_java_vm" id="1156" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_882" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+      <component name="getdown" id="1276" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+    </components>
+  </files>
+  <launchers>
+    <launcher name="Offline Jalview Launcher" id="737" customizedId="" external="false" excludeFromMenu="false" unixMode="755" unixAutoStart="true" menuName="${compiler:sys.shortName}" icnsFile="../../resources/images/jalview_logos.icns" customMacBundleIdentifier="false" macBundleIdentifier="" swtApp="false" fileset="734" macBundleBinary="JavaApplicationStub" addMacEntitlements="false" macEntitlementsFile="" useCustomMacosExecutableName="true" customMacosExecutableName="${compiler:sys.shortName}" useJavaMinVersionOverride="false" javaMinVersionOverride="" useJavaMaxVersionOverride="false" javaMaxVersionOverride="" checkUpdater="false" updateExecutionMode="unattendedProgress" unattendedUpdateTitle="${i18n:updater.WindowTitle(&quot;${compiler:sys.fullName}&quot;)}">
+      <executable name="${compiler:sys.shortName}" type="1" iconSet="true" iconFile="../../resources/images/jalview_logos.ico" executableDir="." redirectStderr="true" stderrFile="error.log" stderrMode="overwrite" redirectStdout="true" stdoutFile="output.log" stdoutMode="overwrite" failOnStderrOutput="true" executableMode="1" changeWorkingDirectory="true" workingDirectory="." singleInstance="true" serviceStartType="2" serviceDependencies="" serviceDescription="" jreLocation="" executionLevel="asInvoker" checkConsoleParameter="true" globalSingleInstance="false" singleInstanceActivate="true" dpiAware="java9+">
+        <versionInfo include="true" fileVersion="" fileDescription="${compiler:sys.shortName}" legalCopyright="..." internalName="${compiler:sys.shortName}" productName="${compiler:sys.shortName}" />
+      </executable>
+      <splashScreen show="false" width="640" height="480" bitmapFile="../../resources/images/jalview_logo_background_fade-640x480.png" textOverlay="true">
+        <text>
+          <statusLine x="85" y="81" text="${compiler:sys.shortName}" fontSize="18" fontColor="0,0,0" bold="false" />
+          <versionLine x="85" y="109" text="version ${compiler:sys.version}" fontSize="8" fontColor="0,0,0" bold="false" />
+        </text>
+      </splashScreen>
+      <java mainClass="com.threerings.getdown.launcher.GetdownApp" mainMode="1" vmParameters="" arguments="." allowVMPassthroughParameters="true" preferredVM="" bundleRuntime="true">
+        <classPath>
+          <archive location="getdown-launcher.jar" failOnError="true" />
+          <archive location="dist/commons-compress-1.18.jar" failOnError="true" />
+        </classPath>
+        <modulePath />
+        <nativeLibraryDirectories />
+        <vmOptions />
+      </java>
+      <includedFiles />
+      <unextractableFiles />
+      <vmOptionsFile mode="template" overwriteMode="0" fileMode="644">
+        <content />
+      </vmOptionsFile>
+      <customScript mode="1" file="">
+        <content />
+      </customScript>
+      <infoPlist mode="1" file="">
+        <content />
+      </infoPlist>
+      <iconImageFiles>
+        <file path="../../resources/images/Jalview_Logo.png" />
+      </iconImageFiles>
+    </launcher>
+    <launcher name="Network Jalview Launcher" id="1402" customizedId="" external="false" excludeFromMenu="false" unixMode="755" unixAutoStart="true" menuName="${compiler:sys.shortName}" icnsFile="../../resources/images/jalview_logos.icns" customMacBundleIdentifier="false" macBundleIdentifier="" swtApp="false" fileset="" macBundleBinary="JavaApplicationStub" addMacEntitlements="false" macEntitlementsFile="" useCustomMacosExecutableName="true" customMacosExecutableName="${compiler:sys.shortName}" useJavaMinVersionOverride="false" javaMinVersionOverride="" useJavaMaxVersionOverride="false" javaMaxVersionOverride="" checkUpdater="false" updateExecutionMode="unattendedProgress" unattendedUpdateTitle="${i18n:updater.WindowTitle(&quot;${compiler:sys.fullName}&quot;)}">
+      <executable name="Jalview" type="1" iconSet="true" iconFile="../../resources/images/jalview_logos.ico" executableDir="." redirectStderr="true" stderrFile="error.log" stderrMode="overwrite" redirectStdout="true" stdoutFile="output.log" stdoutMode="overwrite" failOnStderrOutput="true" executableMode="1" changeWorkingDirectory="true" workingDirectory="." singleInstance="true" serviceStartType="2" serviceDependencies="" serviceDescription="" jreLocation="" executionLevel="asInvoker" checkConsoleParameter="true" globalSingleInstance="false" singleInstanceActivate="true" dpiAware="java9+">
+        <versionInfo include="true" fileVersion="" fileDescription="${compiler:sys.shortName}" legalCopyright="..." internalName="${compiler:sys.shortName}" productName="${compiler:sys.shortName}" />
+      </executable>
+      <splashScreen show="false" width="640" height="480" bitmapFile="../../resources/images/jalview_logo_background_fade-640x480.png" textOverlay="true">
+        <text>
+          <statusLine x="85" y="81" text="${compiler:sys.shortName}" fontSize="18" fontColor="0,0,0" bold="false" />
+          <versionLine x="85" y="109" text="version ${compiler:sys.version}" fontSize="8" fontColor="0,0,0" bold="false" />
+        </text>
+      </splashScreen>
+      <java mainClass="com.threerings.getdown.launcher.GetdownApp" mainMode="1" vmParameters="" arguments="." allowVMPassthroughParameters="true" preferredVM="" bundleRuntime="true">
+        <classPath>
+          <archive location="getdown-launcher.jar" failOnError="true" />
+          <archive location="dist/commons-compress-1.18.jar" failOnError="true" />
+        </classPath>
+        <modulePath />
+        <nativeLibraryDirectories />
+        <vmOptions />
+      </java>
+      <includedFiles />
+      <unextractableFiles />
+      <vmOptionsFile mode="template" overwriteMode="0" fileMode="644">
+        <content />
+      </vmOptionsFile>
+      <customScript mode="1" file="">
+        <content />
+      </customScript>
+      <infoPlist mode="1" file="">
+        <content />
+      </infoPlist>
+      <iconImageFiles>
+        <file path="../../resources/images/Jalview_Logo.png" />
+      </iconImageFiles>
+    </launcher>
+  </launchers>
+  <installerGui installerType="1" addOnAppId="" suggestPreviousLocations="true" autoUpdateDescriptorUrl="" useAutoUpdateBaseUrl="false" autoUpdateBaseUrl="">
+    <staticMembers script="" />
+    <customCode />
+    <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+      <commentFiles />
+      <customAttributes />
+    </autoUpdate>
+    <applications>
+      <application name="" id="installer" customizedId="" beanClass="com.install4j.runtime.beans.applications.InstallerApplication" enabled="true" commentSet="false" comment="" actionElevationType="none" styleId="35" fileset="" customIcnsFile="../../resources/images/jalview_logos.icns" customIcoFile="../../resources/images/jalview_logos.ico" macEntitlementsFile="" automaticLauncherIntegration="false" launchMode="startupFirstWindow" launchInNewProcess="true" launchSchedule="updateSchedule" allLaunchers="true">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.applications.InstallerApplication">
+              <void property="useCustomIcon">
+                <boolean>true</boolean>
+              </void>
+            </object>
+          </java>
+        </serializedBean>
+        <styleOverrides>
+          <styleOverride name="Customize banner image" enabled="true">
+            <group name="" id="146" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+              <serializedBean>
+                <java class="java.beans.XMLDecoder">
+                  <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                    <void property="backgroundColor">
+                      <object class="java.awt.Color">
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="borderSides">
+                      <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                        <void property="bottom">
+                          <boolean>true</boolean>
+                        </void>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBackgroundColor">
+                      <object class="java.awt.Color">
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBorder">
+                      <boolean>true</boolean>
+                    </void>
+                    <void property="imageFile">
+                      <object class="com.install4j.api.beans.ExternalFile">
+                        <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                      </object>
+                    </void>
+                    <void property="insets">
+                      <object class="java.awt.Insets">
+                        <int>5</int>
+                        <int>10</int>
+                        <int>10</int>
+                        <int>10</int>
+                      </object>
+                    </void>
+                  </object>
+                </java>
+              </serializedBean>
+              <beans />
+              <externalParametrizationPropertyNames>
+                <propertyName>imageAnchor</propertyName>
+                <propertyName>imageEdgeBackgroundColor</propertyName>
+                <propertyName>imageFile</propertyName>
+              </externalParametrizationPropertyNames>
+            </group>
+          </styleOverride>
+          <styleOverride name="Jalview" enabled="true">
+            <formComponent name="Watermark" id="352" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="5" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Jalview" externalParametrizationMode="include">
+              <serializedBean>
+                <java class="java.beans.XMLDecoder">
+                  <object class="com.install4j.runtime.beans.formcomponents.SeparatorComponent">
+                    <void property="enabledTitleText">
+                      <boolean>false</boolean>
+                    </void>
+                    <void property="labelText">
+                      <string>install4j</string>
+                    </void>
+                  </object>
+                </java>
+              </serializedBean>
+              <initScript />
+              <visibilityScript />
+              <externalParametrizationPropertyNames>
+                <propertyName>labelText</propertyName>
+              </externalParametrizationPropertyNames>
+            </formComponent>
+          </styleOverride>
+        </styleOverrides>
+        <customScript mode="1" file="">
+          <content />
+        </customScript>
+        <launcherIds />
+        <variables />
+        <startup>
+          <screen name="" id="1" customizedId="" beanClass="com.install4j.runtime.beans.screens.StartupScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.StartupScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="22" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.RequestPrivilegesAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.RequestPrivilegesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents />
+          </screen>
+        </startup>
+        <screens>
+          <screen name="" id="2" customizedId="" beanClass="com.install4j.runtime.beans.screens.WelcomeScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.WelcomeScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides>
+              <styleOverride name="Customize banner image" enabled="true">
+                <group name="" id="145" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+                  <serializedBean>
+                    <java class="java.beans.XMLDecoder">
+                      <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                        <void property="backgroundColor">
+                          <object class="java.awt.Color">
+                            <int>255</int>
+                            <int>255</int>
+                            <int>255</int>
+                            <int>255</int>
+                          </object>
+                        </void>
+                        <void property="borderSides">
+                          <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                            <void property="bottom">
+                              <boolean>true</boolean>
+                            </void>
+                          </object>
+                        </void>
+                        <void property="imageEdgeBackgroundColor">
+                          <object class="java.awt.Color">
+                            <int>25</int>
+                            <int>143</int>
+                            <int>220</int>
+                            <int>255</int>
+                          </object>
+                        </void>
+                        <void property="imageEdgeBorder">
+                          <boolean>true</boolean>
+                        </void>
+                        <void property="imageFile">
+                          <object class="com.install4j.api.beans.ExternalFile">
+                            <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                          </object>
+                        </void>
+                        <void property="insets">
+                          <object class="java.awt.Insets">
+                            <int>5</int>
+                            <int>10</int>
+                            <int>10</int>
+                            <int>10</int>
+                          </object>
+                        </void>
+                      </object>
+                    </java>
+                  </serializedBean>
+                  <beans />
+                  <externalParametrizationPropertyNames>
+                    <propertyName>imageAnchor</propertyName>
+                    <propertyName>imageEdgeBackgroundColor</propertyName>
+                    <propertyName>imageFile</propertyName>
+                  </externalParametrizationPropertyNames>
+                </group>
+              </styleOverride>
+            </styleOverrides>
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="7" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="true" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction">
+                      <void property="excludedVariables">
+                        <array class="java.lang.String" length="1">
+                          <void index="0">
+                            <string>sys.installationDir</string>
+                          </void>
+                        </array>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="3" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:welcomeMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>!context.isConsole()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="4" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent">
+                      <void property="consoleScript">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <void property="value">
+                            <string>String message = context.getMessage("ConsoleWelcomeLabel", context.getApplicationName());
+return console.askOkCancel(message, true);
+</string>
+                          </void>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="5" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.UpdateAlertComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Update Alert" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.UpdateAlertComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>updateCheck</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+              <formComponent name="" id="6" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="20" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${i18n:ClickNext}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="8" customizedId="" beanClass="com.install4j.runtime.beans.screens.InstallationDirectoryScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.InstallationDirectoryScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition>!context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="11" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="true" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction">
+                      <void property="excludedVariables">
+                        <array class="java.lang.String" length="1">
+                          <void index="0">
+                            <string>sys.installationDir</string>
+                          </void>
+                        </array>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>context.getVariable("sys.responseFile") == null</condition>
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="9" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="25" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${i18n:SelectDirLabel(${compiler:sys.fullName})}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="10" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.InstallationDirectoryChooserComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Installation Directory Chooser" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.InstallationDirectoryChooserComponent">
+                      <void property="requestFocus">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>suggestAppDir</propertyName>
+                  <propertyName>validateApplicationId</propertyName>
+                  <propertyName>existingDirWarning</propertyName>
+                  <propertyName>checkWritable</propertyName>
+                  <propertyName>manualEntryAllowed</propertyName>
+                  <propertyName>checkFreeSpace</propertyName>
+                  <propertyName>showRequiredDiskSpace</propertyName>
+                  <propertyName>showFreeDiskSpace</propertyName>
+                  <propertyName>allowSpacesOnUnix</propertyName>
+                  <propertyName>validationScript</propertyName>
+                  <propertyName>standardValidation</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="12" customizedId="" beanClass="com.install4j.runtime.beans.screens.ComponentsScreen" enabled="false" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.ComponentsScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents>
+              <formComponent name="" id="13" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${i18n:SelectComponentsLabel2}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>!context.isConsole()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="14" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ComponentSelectorComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Installation Components" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ComponentSelectorComponent">
+                      <void property="fillVertical">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>selectionChangedScript</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="15" customizedId="" beanClass="com.install4j.runtime.beans.screens.InstallationScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="true" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.InstallationScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="17" customizedId="" beanClass="com.install4j.runtime.beans.actions.InstallFilesAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="2" errorMessage="${i18n:FileCorrupted}">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.InstallFilesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="18" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateProgramGroupAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateProgramGroupAction">
+                      <void property="uninstallerMenuName">
+                        <string>${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>!context.getBooleanVariable("sys.programGroupDisabled")</condition>
+              </action>
+              <action name="" id="19" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.RegisterAddRemoveAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.RegisterAddRemoveAction">
+                      <void property="itemName">
+                        <string>${compiler:sys.fullName} ${compiler:sys.version}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="124" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" enabled="false" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                      <void property="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <void property="value">
+                            <string />
+                          </void>
+                        </object>
+                      </void>
+                      <void property="variableName">
+                        <string />
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>true</condition>
+              </action>
+              <action name="" id="134" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" enabled="false" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction">
+                      <void property="launcherId">
+                        <string>121</string>
+                      </void>
+                      <void property="vmOptions">
+                        <array class="java.lang.String" length="0" />
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="16" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ProgressComponent">
+                      <void property="initialStatusMessage">
+                        <string>${i18n:WizardPreparing}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="20" customizedId="" beanClass="com.install4j.runtime.beans.screens.FinishedScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.FinishedScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="573" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateDesktopLinkAction" enabled="false" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateDesktopLinkAction">
+                      <void property="name">
+                        <string>${compiler:sys.fullName}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>context.getBooleanVariable("createDesktopLinkAction")</condition>
+              </action>
+              <action name="" id="575" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.AddStartupItemAction" enabled="false" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.AddStartupItemAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="576" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.AddToDockAction" enabled="false" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.AddToDockAction" />
+                  </java>
+                </serializedBean>
+                <condition>context.getBooleanVariable("addToDockAction")</condition>
+              </action>
+              <action name="" id="578" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Jalview Project File</string>
+                      </void>
+                      <void property="extension">
+                        <string>jvp</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>121</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>../../resources/images/file.png</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>../../resources/images/file.png</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="21" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="10" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:finishedMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="Add a desktop link" id="574" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                      <void property="checkboxText">
+                        <string>${i18n:CreateDesktopIcon}</string>
+                      </void>
+                      <void property="initiallySelected">
+                        <boolean>true</boolean>
+                      </void>
+                      <void property="variableName">
+                        <string>createDesktopLinkAction</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="Add an executable to the dock" id="577" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                      <void property="checkboxText">
+                        <string>${i18n:AddToDock}</string>
+                      </void>
+                      <void property="initiallySelected">
+                        <boolean>true</boolean>
+                      </void>
+                      <void property="variableName">
+                        <string>addToDockAction</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>Util.isMacOS()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+        </screens>
+      </application>
+      <application name="" id="uninstaller" customizedId="" beanClass="com.install4j.runtime.beans.applications.UninstallerApplication" enabled="true" commentSet="false" comment="" actionElevationType="none" styleId="41" fileset="" customIcnsFile="" customIcoFile="" macEntitlementsFile="" automaticLauncherIntegration="false" launchMode="startupFirstWindow" launchInNewProcess="true" launchSchedule="updateSchedule" allLaunchers="true">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.applications.UninstallerApplication">
+              <void property="customMacosExecutableName">
+                <string>${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}</string>
+              </void>
+              <void property="useCustomMacosExecutableName">
+                <boolean>true</boolean>
+              </void>
+            </object>
+          </java>
+        </serializedBean>
+        <styleOverrides>
+          <styleOverride name="Customize banner image" enabled="true">
+            <group name="" id="147" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+              <serializedBean>
+                <java class="java.beans.XMLDecoder">
+                  <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                    <void property="backgroundColor">
+                      <object class="java.awt.Color">
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="borderSides">
+                      <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                        <void property="bottom">
+                          <boolean>true</boolean>
+                        </void>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBackgroundColor">
+                      <object class="java.awt.Color">
+                        <int>192</int>
+                        <int>192</int>
+                        <int>192</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBorder">
+                      <boolean>true</boolean>
+                    </void>
+                    <void property="imageFile">
+                      <object class="com.install4j.api.beans.ExternalFile">
+                        <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                      </object>
+                    </void>
+                    <void property="insets">
+                      <object class="java.awt.Insets">
+                        <int>5</int>
+                        <int>10</int>
+                        <int>10</int>
+                        <int>10</int>
+                      </object>
+                    </void>
+                  </object>
+                </java>
+              </serializedBean>
+              <beans />
+              <externalParametrizationPropertyNames>
+                <propertyName>imageAnchor</propertyName>
+                <propertyName>imageEdgeBackgroundColor</propertyName>
+                <propertyName>imageFile</propertyName>
+              </externalParametrizationPropertyNames>
+            </group>
+          </styleOverride>
+        </styleOverrides>
+        <customScript mode="1" file="">
+          <content />
+        </customScript>
+        <launcherIds />
+        <variables />
+        <startup>
+          <screen name="" id="23" customizedId="" beanClass="com.install4j.runtime.beans.screens.StartupScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.StartupScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="33" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="34" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.RequireInstallerPrivilegesAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.RequireInstallerPrivilegesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents />
+          </screen>
+        </startup>
+        <screens>
+          <screen name="" id="24" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallWelcomeScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="35" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallWelcomeScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents>
+              <formComponent name="" id="25" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="10" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:welcomeMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>!context.isConsole()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="26" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent">
+                      <void property="consoleScript">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <void property="value">
+                            <string>String message = context.getMessage("ConfirmUninstall", context.getApplicationName());
+return console.askYesNo(message, true);
+</string>
+                          </void>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="27" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallationScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="35" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallationScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="659" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetProgressAction">
+                      <void property="progressChangeType">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.control.ProgressChangeType</class>
+                          <string>SET_INDETERMINATE</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="29" customizedId="" beanClass="com.install4j.runtime.beans.actions.UninstallFilesAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.UninstallFilesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="1603" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetProgressAction">
+                      <void property="progressChangeType">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.control.ProgressChangeType</class>
+                          <string>SET_DETERMINATE</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="1601" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetProgressAction">
+                      <void property="percentValue">
+                        <int>90</int>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="1525" customizedId="" beanClass="com.install4j.runtime.beans.actions.files.DeleteFileAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.files.DeleteFileAction">
+                      <void property="files">
+                        <array class="java.io.File" length="14">
+                          <void index="0">
+                            <object class="java.io.File">
+                              <string>jre</string>
+                            </object>
+                          </void>
+                          <void index="1">
+                            <object class="java.io.File">
+                              <string>.install4j</string>
+                            </object>
+                          </void>
+                          <void index="2">
+                            <object class="java.io.File">
+                              <string>dist</string>
+                            </object>
+                          </void>
+                          <void index="3">
+                            <object class="java.io.File">
+                              <string>resource</string>
+                            </object>
+                          </void>
+                          <void index="4">
+                            <object class="java.io.File">
+                              <string>getdown-launcher.jar</string>
+                            </object>
+                          </void>
+                          <void index="5">
+                            <object class="java.io.File">
+                              <string>getdown-launcher-old.jar</string>
+                            </object>
+                          </void>
+                          <void index="6">
+                            <object class="java.io.File">
+                              <string>getdown-launcher-new.jar</string>
+                            </object>
+                          </void>
+                          <void index="7">
+                            <object class="java.io.File">
+                              <string>digest.txt</string>
+                            </object>
+                          </void>
+                          <void index="8">
+                            <object class="java.io.File">
+                              <string>digest2.txt</string>
+                            </object>
+                          </void>
+                          <void index="9">
+                            <object class="java.io.File">
+                              <string>getdown.txt</string>
+                            </object>
+                          </void>
+                          <void index="10">
+                            <object class="java.io.File">
+                              <string>getdown-launcher.jarv</string>
+                            </object>
+                          </void>
+                          <void index="11">
+                            <object class="java.io.File">
+                              <string>gettingdown.lock</string>
+                            </object>
+                          </void>
+                          <void index="12">
+                            <object class="java.io.File">
+                              <string>launcher.log</string>
+                            </object>
+                          </void>
+                          <void index="13">
+                            <object class="java.io.File">
+                              <string>proxy.txt</string>
+                            </object>
+                          </void>
+                        </array>
+                      </void>
+                      <void property="recursive">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="660" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetProgressAction">
+                      <void property="percentValue">
+                        <int>100</int>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="28" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ProgressComponent">
+                      <void property="initialStatusMessage">
+                        <string>${i18n:UninstallerPreparing}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="32" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallFailureScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="35" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallFailureScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents />
+          </screen>
+          <screen name="" id="30" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallSuccessScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="35" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallSuccessScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents>
+              <formComponent name="" id="31" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="10" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:successMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+        </screens>
+      </application>
+    </applications>
+    <styles defaultStyleId="35">
+      <style name="Standard" id="35" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.styles.FormStyle" />
+          </java>
+        </serializedBean>
+        <formComponents>
+          <formComponent name="Header" id="36" customizedId="" beanClass="com.install4j.runtime.beans.styles.NestedStyleComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.NestedStyleComponent">
+                  <void property="styleId">
+                    <string>48</string>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <initScript />
+            <visibilityScript />
+            <externalParametrizationPropertyNames />
+          </formComponent>
+          <group name="Main" id="37" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                  <void property="imageEdgeAxisType">
+                    <object class="java.lang.Enum" method="valueOf">
+                      <class>com.install4j.runtime.beans.formcomponents.AxisType</class>
+                      <string>HORIZONTAL</string>
+                    </object>
+                  </void>
+                  <void property="imageFile">
+                    <object class="com.install4j.api.beans.ExternalFile">
+                      <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                    </object>
+                  </void>
+                  <void property="imageOverlap">
+                    <boolean>true</boolean>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <beans>
+              <formComponent name="" id="38" customizedId="" beanClass="com.install4j.runtime.beans.styles.ContentComponent" enabled="true" commentSet="false" comment="" insetTop="10" insetLeft="20" insetBottom="10" insetRight="20" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.ContentComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="Watermark" id="39" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="5" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Jalview" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.SeparatorComponent">
+                      <void property="enabledTitleText">
+                        <boolean>false</boolean>
+                      </void>
+                      <void property="labelText">
+                        <string>install4j</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>labelText</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+              <formComponent name="Footer" id="40" customizedId="" beanClass="com.install4j.runtime.beans.styles.NestedStyleComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.NestedStyleComponent">
+                      <void property="styleId">
+                        <string>52</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </beans>
+            <externalParametrizationPropertyNames />
+          </group>
+        </formComponents>
+      </style>
+      <style name="Banner" id="41" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.styles.FormStyle" />
+          </java>
+        </serializedBean>
+        <formComponents>
+          <group name="" id="42" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                  <void property="backgroundColor">
+                    <object class="java.awt.Color">
+                      <int>255</int>
+                      <int>255</int>
+                      <int>255</int>
+                      <int>255</int>
+                    </object>
+                  </void>
+                  <void property="borderSides">
+                    <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                      <void property="bottom">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </void>
+                  <void property="imageEdgeBackgroundColor">
+                    <object class="java.awt.Color">
+                      <int>25</int>
+                      <int>143</int>
+                      <int>220</int>
+                      <int>255</int>
+                    </object>
+                  </void>
+                  <void property="imageEdgeBorder">
+                    <boolean>true</boolean>
+                  </void>
+                  <void property="imageFile">
+                    <object class="com.install4j.api.beans.ExternalFile">
+                      <string>${compiler:sys.install4jHome}/resource/styles/wizard.png</string>
+                    </object>
+                  </void>
+                  <void property="insets">
+                    <object class="java.awt.Insets">
+                      <int>5</int>
+                      <int>10</int>
+                      <int>10</int>
+                      <int>10</int>
+                    </object>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <beans>
+              <formComponent name="" id="43" customizedId="" beanClass="com.install4j.runtime.beans.styles.ScreenTitleComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.ScreenTitleComponent">
+                      <void property="labelFontSizePercent">
+                        <int>130</int>
+                      </void>
+                      <void property="labelFontStyle">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.formcomponents.FontStyle</class>
+                          <string>BOLD</string>
+                        </object>
+                      </void>
+                      <void property="labelFontType">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.formcomponents.FontType</class>
+                          <string>DERIVED</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="44" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.SeparatorComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="45" customizedId="" beanClass="com.install4j.runtime.beans.styles.ContentComponent" enabled="true" commentSet="false" comment="" insetTop="10" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.ContentComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </beans>
+            <externalParametrizationPropertyNames>
+              <propertyName>imageAnchor</propertyName>
+              <propertyName>imageEdgeBackgroundColor</propertyName>
+              <propertyName>imageFile</propertyName>
+            </externalParametrizationPropertyNames>
+          </group>
+          <formComponent name="" id="46" customizedId="" beanClass="com.install4j.runtime.beans.styles.NestedStyleComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.NestedStyleComponent">
+                  <void property="styleId">
+                    <string>52</string>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <initScript />
+            <visibilityScript />
+            <externalParametrizationPropertyNames />
+          </formComponent>
+        </formComponents>
+      </style>
+      <group name="Style components" id="47" customizedId="" beanClass="com.install4j.runtime.beans.groups.StyleGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.groups.StyleGroup" />
+          </java>
+        </serializedBean>
+        <beans>
+          <style name="Standard header" id="48" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.FormStyle">
+                  <void property="fillVertical">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="standalone">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="verticalAnchor">
+                    <object class="java.lang.Enum" method="valueOf">
+                      <class>com.install4j.api.beans.Anchor</class>
+                      <string>NORTH</string>
+                    </object>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <formComponents>
+              <group name="" id="49" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize title bar" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                      <void property="backgroundColor">
+                        <object class="java.awt.Color">
+                          <int>255</int>
+                          <int>255</int>
+                          <int>255</int>
+                          <int>255</int>
+                        </object>
+                      </void>
+                      <void property="borderSides">
+                        <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                          <void property="bottom">
+                            <boolean>true</boolean>
+                          </void>
+                        </object>
+                      </void>
+                      <void property="imageAnchor">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.api.beans.Anchor</class>
+                          <string>NORTHEAST</string>
+                        </object>
+                      </void>
+                      <void property="imageEdgeBorderWidth">
+                        <int>2</int>
+                      </void>
+                      <void property="imageFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>icon:${installer:sys.installerApplicationMode}_header.png</string>
+                        </object>
+                      </void>
+                      <void property="imageInsets">
+                        <object class="java.awt.Insets">
+                          <int>0</int>
+                          <int>5</int>
+                          <int>1</int>
+                          <int>1</int>
+                        </object>
+                      </void>
+                      <void property="insets">
+                        <object class="java.awt.Insets">
+                          <int>0</int>
+                          <int>20</int>
+                          <int>0</int>
+                          <int>10</int>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <beans>
+                  <formComponent name="Title" id="50" customizedId="" beanClass="com.install4j.runtime.beans.styles.ScreenTitleComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.ScreenTitleComponent">
+                          <void property="labelFontStyle">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.runtime.beans.formcomponents.FontStyle</class>
+                              <string>BOLD</string>
+                            </object>
+                          </void>
+                          <void property="labelFontType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.runtime.beans.formcomponents.FontType</class>
+                              <string>DERIVED</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Subtitle" id="51" customizedId="" beanClass="com.install4j.runtime.beans.styles.ScreenTitleComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="8" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.ScreenTitleComponent">
+                          <void property="titleType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.runtime.beans.styles.TitleType</class>
+                              <string>SUB_TITLE</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                </beans>
+                <externalParametrizationPropertyNames>
+                  <propertyName>backgroundColor</propertyName>
+                  <propertyName>foregroundColor</propertyName>
+                  <propertyName>imageAnchor</propertyName>
+                  <propertyName>imageFile</propertyName>
+                  <propertyName>imageOverlap</propertyName>
+                </externalParametrizationPropertyNames>
+              </group>
+            </formComponents>
+          </style>
+          <style name="Standard footer" id="52" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.FormStyle">
+                  <void property="fillVertical">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="standalone">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="verticalAnchor">
+                    <object class="java.lang.Enum" method="valueOf">
+                      <class>com.install4j.api.beans.Anchor</class>
+                      <string>SOUTH</string>
+                    </object>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <formComponents>
+              <group name="" id="53" customizedId="" beanClass="com.install4j.runtime.beans.groups.HorizontalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.groups.HorizontalFormComponentGroup">
+                      <void property="alignFirstLabel">
+                        <boolean>false</boolean>
+                      </void>
+                      <void property="insets">
+                        <object class="java.awt.Insets">
+                          <int>3</int>
+                          <int>5</int>
+                          <int>8</int>
+                          <int>5</int>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <beans>
+                  <formComponent name="" id="54" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SpringComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.formcomponents.SpringComponent" />
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Back button" id="55" customizedId="" beanClass="com.install4j.runtime.beans.styles.StandardControlButtonComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.StandardControlButtonComponent">
+                          <void property="buttonText">
+                            <string>&lt; ${i18n:ButtonBack}</string>
+                          </void>
+                          <void property="controlButtonType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.api.context.ControlButtonType</class>
+                              <string>PREVIOUS</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Next button" id="56" customizedId="" beanClass="com.install4j.runtime.beans.styles.StandardControlButtonComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.StandardControlButtonComponent">
+                          <void property="buttonText">
+                            <string>${i18n:ButtonNext} &gt;</string>
+                          </void>
+                          <void property="controlButtonType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.api.context.ControlButtonType</class>
+                              <string>NEXT</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Cancel button" id="57" customizedId="" beanClass="com.install4j.runtime.beans.styles.StandardControlButtonComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="5" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.StandardControlButtonComponent">
+                          <void property="buttonText">
+                            <string>${i18n:ButtonCancel}</string>
+                          </void>
+                          <void property="controlButtonType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.api.context.ControlButtonType</class>
+                              <string>CANCEL</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                </beans>
+                <externalParametrizationPropertyNames />
+              </group>
+            </formComponents>
+          </style>
+        </beans>
+      </group>
+    </styles>
+  </installerGui>
+  <mediaSets>
+    <linuxDeb name="Linux Deb Archive" id="153" customizedId="" mediaFileName="" installDir="/opt/${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" overwriteNeverAsConfigFiles="false" dependencies="" bzip="true" description="Jalview Desktop" maintainerEmail="help@jalview.org" architectureSet="false" architecture="">
+      <excludedComponents>
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1276" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="1402" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <preInstallScript mode="1" file="">
+        <content />
+      </preInstallScript>
+      <postInstallScript mode="1" file="">
+        <content />
+      </postInstallScript>
+      <preUninstallScript mode="1" file="">
+        <content />
+      </preUninstallScript>
+      <postUninstallScript mode="1" file="">
+        <content />
+      </postUninstallScript>
+    </linuxDeb>
+    <linuxRPM name="Linux RPM" id="570" customizedId="" mediaFileName="" installDir="/opt/${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" overwriteNeverAsConfigFiles="false" dependencies="" os="linux" arch="i386">
+      <excludedComponents>
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1276" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="1402" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <preInstallScript mode="1" file="">
+        <content />
+      </preInstallScript>
+      <postInstallScript mode="1" file="">
+        <content />
+      </postInstallScript>
+      <preUninstallScript mode="1" file="">
+        <content />
+      </preUninstallScript>
+      <postUninstallScript mode="1" file="">
+        <content />
+      </postUninstallScript>
+    </linuxRPM>
+    <windows name="Offline Windows" id="743" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="64" runPostProcessor="true" postProcessor="${compiler:JSIGN_SH} $EXECUTABLE" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="$$WINDOWS_JAVA_VM_TGZ$$" manualJREEntry="true" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="" contentFilesType="1" verifyIntegrity="true">
+      <excludedComponents>
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1276" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="1402" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+    </windows>
+    <macosArchive name="Offline macOS Single Bundle Archive" id="878" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}-app_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" archiveType="dmg" volumeName="${compiler:sys.shortName} Installer" launcherId="737">
+      <excludedComponents>
+        <component id="1156" />
+        <component id="1276" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <topLevelFiles>
+        <symlink name="&quot; &quot;" target="/Applications" />
+        <file name=".background/jalview_dmg_background.png" file="./jalview_dmg_background.png" />
+        <file name=".DS_Store" file="./DS_Store" />
+        <symlink name="Jalview.app/Contents/Resources/app/jre/Contents/MacOS/libjli.dylib" target="../Home/lib/jli/libjli.dylib" />
+      </topLevelFiles>
+    </macosArchive>
+    <windows name="Network Windows" id="1272" customizedId="" mediaFileName="${compiler:sys.shortName}-NETWORK_${compiler:sys.platform}_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="64" runPostProcessor="true" postProcessor="${compiler:JSIGN_SH} $EXECUTABLE" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="$$WINDOWS_JAVA_VM_TGZ$$" manualJREEntry="true" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="" contentFilesType="1" verifyIntegrity="true">
+      <excludedComponents>
+        <component id="1031" />
+        <component id="1155" />
+        <component id="1156" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="737" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+    </windows>
+    <macosArchive name="Network macOS Single Bundle Archive" id="1274" customizedId="" mediaFileName="${compiler:sys.shortName}-NETWORK_${compiler:sys.platform}-app_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" archiveType="dmg" volumeName="${compiler:sys.shortName} Installer" launcherId="1402">
+      <excludedComponents>
+        <component id="1031" />
+        <component id="1156" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <topLevelFiles>
+        <symlink name="&quot; &quot;" target="/Applications" />
+        <file name=".background/jalview_dmg_background.png" file="./jalview_dmg_background.png" />
+        <file name=".DS_Store" file="./DS_Store" />
+        <symlink name="Jalview.app/Contents/Resources/app/jre/Contents/MacOS/libjli.dylib" target="../Home/lib/jli/libjli.dylib" />
+      </topLevelFiles>
+    </macosArchive>
+    <unixInstaller name="Unix Installer" id="1595" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}_installer_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="" contentFilesType="1">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedLaunchers />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <installerScript mode="1" file="">
+        <content />
+      </installerScript>
+    </unixInstaller>
+    <unixArchive name="Unix Archive" id="1596" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}_archive_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false">
+      <excludedComponents>
+        <component id="1031" />
+        <component id="1155" />
+        <component id="1156" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude />
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+    </unixArchive>
+    <windows name="Non-registry Windows offline" id="1600" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE-noreg_${compiler:sys.platform}_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="64" runPostProcessor="true" postProcessor="${compiler:JSIGN_SH} $EXECUTABLE" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="$$WINDOWS_JAVA_VM_TGZ$$" manualJREEntry="true" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="" contentFilesType="1" verifyIntegrity="true">
+      <excludedComponents>
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1276" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="1402" />
+      </excludedLaunchers>
+      <excludedBeans>
+        <bean refId="18" />
+        <bean refId="19" />
+        <bean refId="578" />
+      </excludedBeans>
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+    </windows>
+  </mediaSets>
+  <buildIds buildAll="true">
+    <mediaSet refId="153" />
+    <mediaSet refId="570" />
+    <mediaSet refId="743" />
+    <mediaSet refId="878" />
+    <mediaSet refId="1272" />
+    <mediaSet refId="1274" />
+    <mediaSet refId="1595" />
+    <mediaSet refId="1596" />
+    <mediaSet refId="1598" />
+    <mediaSet refId="1600" />
+  </buildIds>
+  <buildOptions verbose="false" faster="false" disableSigning="false" disableJreBundling="false" debug="false" />
+</install4j>
diff --git a/utils/install4j/file_associations_auto-Info_plist.xml b/utils/install4j/file_associations_auto-Info_plist.xml
new file mode 100644 (file)
index 0000000..da2633b
--- /dev/null
@@ -0,0 +1,449 @@
+<key>CFBundleDocumentTypes</key>
+<array>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>jvp</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Jalview File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-jalview+xml+zip</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>jvl</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Jalview Version Locator File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-Version-Locator.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-jalview-jvl+text</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>amsa</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>AMSA File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-amsa</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>annotations</string>
+<string>jvannotations</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Jalview Annotations File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-jalview-annotations+text</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>biojson</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>BioJSON File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-jalview-biojson+json</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>BLC</string>
+<string>blc</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>BLC File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-blc</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>aln</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Clustal File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-clustal</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>fa</string>
+<string>fasta</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Fasta File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-fasta</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>features</string>
+<string>jvfeatures</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Jalview Features File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-jalview-features+text</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>gff2</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Generic Features Format v2 File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-gff2</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>gff3</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Generic Features Format v3 File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-gff3</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>concise</string>
+<string>jnet</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>JnetFile File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-jalview-jnet+text</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>cif</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>mmCIF File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>chemical/x-cif</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>mcif</string>
+<string>mmcif</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>mmCIF File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>chemical/x-mcif</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>msf</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>MSF File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-msf</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>pdb</string>
+<string>ent</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>PDB File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>chemical/x-pdb</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>pfam</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>PFAM File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-pfam</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>phy</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>PHYLIP File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-phylip</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>pileup</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>PileUp File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-pileup</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>pir</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>PIR File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-pir</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>rnaml</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>RNAML File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/rnaml+xml</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>mat</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Substitution Matrix File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-jalview-scorematrix+text</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+<string>sto</string>
+<string>stk</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>Stockholm File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>text/x-stockholm</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+</array>
diff --git a/utils/install4j/file_associations_auto-install4j.xml b/utils/install4j/file_associations_auto-install4j.xml
new file mode 100644 (file)
index 0000000..192b5d2
--- /dev/null
@@ -0,0 +1,1085 @@
+              <action name="" id="10000" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .jvp file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Jalview File</string>
+                      </void>
+                      <void property="extension">
+                        <string>jvp</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10001" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .jvl file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Jalview Version Locator File</string>
+                      </void>
+                      <void property="extension">
+                        <string>jvl</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-Version-Locator.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-Version-Locator.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10002" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .amsa file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>AMSA File</string>
+                      </void>
+                      <void property="extension">
+                        <string>amsa</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10003" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .annotations file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Jalview Annotations File</string>
+                      </void>
+                      <void property="extension">
+                        <string>annotations</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10004" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .jvannotations file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Jalview Annotations File</string>
+                      </void>
+                      <void property="extension">
+                        <string>jvannotations</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10005" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .biojson file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>BioJSON File</string>
+                      </void>
+                      <void property="extension">
+                        <string>biojson</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10006" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .BLC file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>BLC File</string>
+                      </void>
+                      <void property="extension">
+                        <string>BLC</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10007" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .blc file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>BLC File</string>
+                      </void>
+                      <void property="extension">
+                        <string>blc</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10008" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .aln file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Clustal File</string>
+                      </void>
+                      <void property="extension">
+                        <string>aln</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10009" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .fa file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Fasta File</string>
+                      </void>
+                      <void property="extension">
+                        <string>fa</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10010" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .fasta file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Fasta File</string>
+                      </void>
+                      <void property="extension">
+                        <string>fasta</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10011" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .features file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Jalview Features File</string>
+                      </void>
+                      <void property="extension">
+                        <string>features</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10012" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .jvfeatures file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Jalview Features File</string>
+                      </void>
+                      <void property="extension">
+                        <string>jvfeatures</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10013" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .gff2 file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Generic Features Format v2 File</string>
+                      </void>
+                      <void property="extension">
+                        <string>gff2</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10014" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .gff3 file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Generic Features Format v3 File</string>
+                      </void>
+                      <void property="extension">
+                        <string>gff3</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10015" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .concise file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>JnetFile File</string>
+                      </void>
+                      <void property="extension">
+                        <string>concise</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10016" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .jnet file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>JnetFile File</string>
+                      </void>
+                      <void property="extension">
+                        <string>jnet</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10017" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .cif file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>mmCIF File</string>
+                      </void>
+                      <void property="extension">
+                        <string>cif</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10018" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .mcif file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>mmCIF File</string>
+                      </void>
+                      <void property="extension">
+                        <string>mcif</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10019" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .mmcif file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>mmCIF File</string>
+                      </void>
+                      <void property="extension">
+                        <string>mmcif</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10020" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .msf file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>MSF File</string>
+                      </void>
+                      <void property="extension">
+                        <string>msf</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10021" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .pdb file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>PDB File</string>
+                      </void>
+                      <void property="extension">
+                        <string>pdb</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10022" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .ent file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>PDB File</string>
+                      </void>
+                      <void property="extension">
+                        <string>ent</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10023" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .pfam file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>PFAM File</string>
+                      </void>
+                      <void property="extension">
+                        <string>pfam</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10024" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .phy file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>PHYLIP File</string>
+                      </void>
+                      <void property="extension">
+                        <string>phy</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10025" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .pileup file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>PileUp File</string>
+                      </void>
+                      <void property="extension">
+                        <string>pileup</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10026" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .pir file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>PIR File</string>
+                      </void>
+                      <void property="extension">
+                        <string>pir</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10027" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .rnaml file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>RNAML File</string>
+                      </void>
+                      <void property="extension">
+                        <string>rnaml</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10028" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .mat file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Substitution Matrix File</string>
+                      </void>
+                      <void property="extension">
+                        <string>mat</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10029" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .sto file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Stockholm File</string>
+                      </void>
+                      <void property="extension">
+                        <string>sto</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
+              <action name="" id="10030" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .stk file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Stockholm File</string>
+                      </void>
+                      <void property="extension">
+                        <string>stk</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
diff --git a/utils/install4j/file_associations_template-Info_plist.xml b/utils/install4j/file_associations_template-Info_plist.xml
new file mode 100644 (file)
index 0000000..7fa4fed
--- /dev/null
@@ -0,0 +1,19 @@
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
+$$<string>EXTENSIONS</string>
+$$</array>
+<key>CFBundleTypeName</key>
+<string>$$NAME$$ File</string>
+<key>CFBundleTypeIconFile</key>
+<string>$$ICONFILE$$.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>$$MIMETYPE$$</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
diff --git a/utils/install4j/file_associations_template-install4j.xml b/utils/install4j/file_associations_template-install4j.xml
new file mode 100644 (file)
index 0000000..697029a
--- /dev/null
@@ -0,0 +1,35 @@
+              <action name="" id="$$ID$$" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make .$$EXTENSION$$ file association">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>$$NAME$$ File</string>
+                      </void>
+                      <void property="extension">
+                        <string>$$EXTENSION$$</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>737</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>$$ICONFILE$$.icns</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>$$ICONFILE$$.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+
diff --git a/utils/install4j/install4j_template.install4j b/utils/install4j/install4j_template.install4j
new file mode 100644 (file)
index 0000000..d1c85e9
--- /dev/null
@@ -0,0 +1,2140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<install4j version="7.0.11" transformSequenceNumber="7">
+  <directoryPresets config="." />
+  <application name="Jalview" distributionSourceDir="" applicationId="6595-2347-1923-0725" mediaDir="../../build/install4j" mediaFilePattern="${compiler:sys.shortName}_${compiler:sys.platform}_${compiler:sys.version}" compression="6" lzmaCompression="true" pack200Compression="false" excludeSignedFromPacking="true" commonExternalFiles="false" createMd5Sums="true" shrinkRuntime="true" shortName="Jalview" publisher="University of Dundee" publisherWeb="http://www.jalview.org/" version="$$VERSION$$" allPathsRelative="true" backupOnSave="false" autoSave="false" convertDotsToUnderscores="true" macSignature="????" macVolumeId="5aac4968c304f65" javaMinVersion="9999999999" javaMaxVersion="" allowBetaVM="true" jdkMode="jdk" jdkName="JDK 11.0">
+    <languages skipLanguageSelection="false" languageSelectionInPrincipalLanguage="false">
+      <principalLanguage id="en" customLocalizationFile="" />
+      <additionalLanguages />
+    </languages>
+    <searchSequence>
+      <directory location="${compiler:JRE_DIR}" />
+      <registry />
+      <envVar name="JAVA_HOME" />
+    </searchSequence>
+    <variables>
+      <variable name="OSX_KEYSTORE" value="" description="" category="" />
+      <variable name="JSIGN_SH" value="" description="" category="" />
+      <variable name="JRE_DIR" value="jre" description="The folder under the app folder that the JRE will be either copied or unpacked into" category="" />
+    </variables>
+    <mergedProjects />
+    <codeSigning macEnabled="true" macPkcs12File="${compiler:OSX_KEYSTORE}" windowsEnabled="false" windowsKeySource="pkcs12" windowsPvkFile="" windowsSpcFile="" windowsPkcs12File="" windowsPkcs11Library="" windowsPkcs11Slot="">
+      <windowsKeystoreIdentifier issuer="" serial="" subject="" />
+      <windowsPkcs11Identifier issuer="" serial="" subject="" />
+    </codeSigning>
+  </application>
+  <files keepModificationTimes="false" missingFilesStrategy="warn" globalExcludeSuffixes="" defaultOverwriteMode="4" defaultUninstallMode="2" launcherOverwriteMode="3" defaultFileMode="644" defaultDirMode="755">
+    <filesets>
+      <fileset name="Full file set" id="734" customizedId="" />
+      <fileset name="Mac OS X JRE" id="880" customizedId="" />
+      <fileset name="Windows JRE" id="882" customizedId="" />
+      <fileset name="Jalview application" id="1873" customizedId="" />
+      <fileset name="MacOS Old Jalview Uninstaller" id="2105" customizedId="" />
+    </filesets>
+    <roots>
+      <root id="735" fileset="734" location="" />
+      <root id="881" fileset="880" location="" />
+      <root id="883" fileset="882" location="" />
+      <root id="1874" fileset="1873" location="" />
+      <root id="2106" fileset="2105" location="" />
+    </roots>
+    <mountPoints>
+      <mountPoint id="454" root="" location="" mode="755" />
+      <mountPoint id="736" root="735" location="" mode="755" />
+      <mountPoint id="884" root="881" location="" mode="755" />
+      <mountPoint id="885" root="883" location="" mode="755" />
+      <mountPoint id="1875" root="1874" location="" mode="755" />
+      <mountPoint id="2107" root="2106" location="" mode="755" />
+    </mountPoints>
+    <entries>
+      <dirEntry mountPoint="454" file="../../getdown/files/$$JAVA_VERSION$$" overwriteMode="4" shared="false" fileMode="644" uninstallMode="2" overrideFileMode="false" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="direct" subDirectory="files" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="736" file="../../getdown/website/$$JAVA_VERSION$$" overwriteMode="4" shared="false" fileMode="644" uninstallMode="2" overrideFileMode="false" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="direct" subDirectory="files" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="884" file="$$MACOS_JAVA_VM_DIR$$" overwriteMode="4" shared="false" fileMode="755" uninstallMode="0" overrideFileMode="true" overrideOverwriteMode="false" overrideUninstallMode="true" entryMode="subdir" subDirectory="${compiler:JRE_DIR}" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="885" file="$$WINDOWS_JAVA_VM_DIR$$" overwriteMode="4" shared="false" fileMode="755" uninstallMode="0" overrideFileMode="true" overrideOverwriteMode="false" overrideUninstallMode="true" entryMode="subdir" subDirectory="${compiler:JRE_DIR}" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="1875" file="../../getdown/website/$$JAVA_VERSION$$/$$GETDOWN_DIST_DIR$$" overwriteMode="1" shared="false" fileMode="644" uninstallMode="2" overrideFileMode="true" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="subdir" subDirectory="$$GETDOWN_DIST_DIR$$" excludeSuffixes="" dirMode="755" overrideDirMode="true">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="2107" file="Uninstall Old Jalview.app" overwriteMode="4" shared="false" fileMode="755" uninstallMode="0" overrideFileMode="true" overrideOverwriteMode="false" overrideUninstallMode="false" entryMode="subdir" subDirectory="Uninstall Old Jalview.app" excludeSuffixes="" dirMode="755" overrideDirMode="true">
+        <exclude />
+      </dirEntry>
+    </entries>
+    <components>
+      <component name="jalview_getdown" id="1031" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_734" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+      <component name="macos_java_vm" id="1155" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_880" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+      <component name="windows_java_vm" id="1156" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_882" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+      <component name="getdown" id="1276" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+      <component name="jalview" id="1881" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_1873" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+      <component name="macos_old_jalview_uninstaller" id="2110" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_2105" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+    </components>
+  </files>
+  <launchers>
+    <launcher name="Offline Jalview Launcher" id="737" customizedId="" external="false" excludeFromMenu="false" unixMode="755" unixAutoStart="true" menuName="${compiler:sys.shortName}" icnsFile="../../resources/images/jalview_logos.icns" customMacBundleIdentifier="true" macBundleIdentifier="$$MACOS_BUNDLE_ID$$" swtApp="false" fileset="734" macBundleBinary="JavaApplicationStub" addMacEntitlements="false" macEntitlementsFile="" useCustomMacosExecutableName="true" customMacosExecutableName="${compiler:sys.shortName}" useJavaMinVersionOverride="false" javaMinVersionOverride="" useJavaMaxVersionOverride="false" javaMaxVersionOverride="" checkUpdater="false" updateExecutionMode="unattendedProgress" unattendedUpdateTitle="${i18n:updater.WindowTitle(&quot;${compiler:sys.fullName}&quot;)}">
+      <executable name="${compiler:sys.shortName}" type="1" iconSet="true" iconFile="../../resources/images/jalview_logos.ico" executableDir="." redirectStderr="true" stderrFile="error.log" stderrMode="overwrite" redirectStdout="true" stdoutFile="output.log" stdoutMode="overwrite" failOnStderrOutput="true" executableMode="1" changeWorkingDirectory="true" workingDirectory="." singleInstance="true" serviceStartType="2" serviceDependencies="" serviceDescription="" jreLocation="" executionLevel="asInvoker" checkConsoleParameter="true" globalSingleInstance="false" singleInstanceActivate="true" dpiAware="java9+">
+        <versionInfo include="true" fileVersion="" fileDescription="${compiler:sys.shortName}" legalCopyright="$$COPYRIGHT_MESSAGE$$" internalName="${compiler:sys.shortName}" productName="${compiler:sys.shortName}" />
+      </executable>
+      <splashScreen show="false" width="640" height="480" bitmapFile="../../resources/images/jalview_logo_background_fade-640x480.png" textOverlay="true">
+        <text>
+          <statusLine x="85" y="81" text="${compiler:sys.shortName}" fontSize="18" fontColor="0,0,0" bold="false" />
+          <versionLine x="85" y="109" text="version ${compiler:sys.version}" fontSize="8" fontColor="0,0,0" bold="false" />
+        </text>
+      </splashScreen>
+      <java mainClass="com.threerings.getdown.launcher.GetdownApp" mainMode="1" vmParameters="" arguments=". jalview" allowVMPassthroughParameters="true" preferredVM="" bundleRuntime="true">
+        <classPath>
+          <archive location="getdown-launcher.jar" failOnError="true" />
+          <scanDirectory location="$$GETDOWN_INSTALL_DIR$$" failOnError="false" />
+        </classPath>
+        <modulePath />
+        <nativeLibraryDirectories />
+        <vmOptions />
+      </java>
+      <includedFiles />
+      <unextractableFiles />
+      <vmOptionsFile mode="template" overwriteMode="0" fileMode="644">
+        <content />
+      </vmOptionsFile>
+      <customScript mode="1" file="">
+        <content />
+      </customScript>
+      <infoPlist mode="2" file="$$INSTALL4JINFOPLISTFILEASSOCIATIONS$$">
+        <content />
+      </infoPlist>
+      <iconImageFiles>
+        <file path="../../resources/images/JalviewLogo_Huge.png" />
+      </iconImageFiles>
+    </launcher>
+    <launcher name="Network Jalview Launcher" id="1402" customizedId="" external="false" excludeFromMenu="false" unixMode="755" unixAutoStart="true" menuName="${compiler:sys.shortName}" icnsFile="../../resources/images/jalview_logos.icns" customMacBundleIdentifier="true" macBundleIdentifier="$$MACOS_BUNDLE_ID$$" swtApp="false" fileset="" macBundleBinary="JavaApplicationStub" addMacEntitlements="false" macEntitlementsFile="" useCustomMacosExecutableName="true" customMacosExecutableName="${compiler:sys.shortName}" useJavaMinVersionOverride="false" javaMinVersionOverride="" useJavaMaxVersionOverride="false" javaMaxVersionOverride="" checkUpdater="false" updateExecutionMode="unattendedProgress" unattendedUpdateTitle="${i18n:updater.WindowTitle(&quot;${compiler:sys.fullName}&quot;)}">
+      <executable name="Jalview" type="1" iconSet="true" iconFile="../../resources/images/jalview_logos.ico" executableDir="." redirectStderr="true" stderrFile="error.log" stderrMode="overwrite" redirectStdout="true" stdoutFile="output.log" stdoutMode="overwrite" failOnStderrOutput="true" executableMode="1" changeWorkingDirectory="true" workingDirectory="." singleInstance="true" serviceStartType="2" serviceDependencies="" serviceDescription="" jreLocation="" executionLevel="asInvoker" checkConsoleParameter="true" globalSingleInstance="false" singleInstanceActivate="true" dpiAware="java9+">
+        <versionInfo include="true" fileVersion="" fileDescription="${compiler:sys.shortName}" legalCopyright="$$COPYRIGHT_MESSAGE$$" internalName="${compiler:sys.shortName}" productName="${compiler:sys.shortName}" />
+      </executable>
+      <splashScreen show="false" width="640" height="480" bitmapFile="../../resources/images/jalview_logo_background_fade-640x480.png" textOverlay="true">
+        <text>
+          <statusLine x="85" y="81" text="${compiler:sys.shortName}" fontSize="18" fontColor="0,0,0" bold="false" />
+          <versionLine x="85" y="109" text="version ${compiler:sys.version}" fontSize="8" fontColor="0,0,0" bold="false" />
+        </text>
+      </splashScreen>
+      <java mainClass="com.threerings.getdown.launcher.GetdownApp" mainMode="1" vmParameters="" arguments=". jalview" allowVMPassthroughParameters="true" preferredVM="" bundleRuntime="true">
+        <classPath>
+          <archive location="getdown-launcher.jar" failOnError="true" />
+          <scanDirectory location="$$GETDOWN_INSTALL_DIR$$" failOnError="false" />
+        </classPath>
+        <modulePath />
+        <nativeLibraryDirectories />
+        <vmOptions />
+      </java>
+      <includedFiles />
+      <unextractableFiles />
+      <vmOptionsFile mode="template" overwriteMode="0" fileMode="644">
+        <content />
+      </vmOptionsFile>
+      <customScript mode="1" file="">
+        <content />
+      </customScript>
+      <infoPlist mode="2" file="$$INSTALL4JINFOPLISTFILEASSOCIATIONS$$">
+        <content />
+      </infoPlist>
+      <iconImageFiles>
+        <file path="../../resources/images/JalviewLogo_Huge.png" />
+      </iconImageFiles>
+    </launcher>
+    <launcher name="Jalview package" id="1876" customizedId="" external="false" excludeFromMenu="false" unixMode="755" unixAutoStart="true" menuName="${compiler:sys.shortName} $$VERSION$$" icnsFile="" customMacBundleIdentifier="false" macBundleIdentifier="" swtApp="false" fileset="1873" macBundleBinary="JavaApplicationStub" addMacEntitlements="false" macEntitlementsFile="" useCustomMacosExecutableName="false" customMacosExecutableName="" useJavaMinVersionOverride="false" javaMinVersionOverride="" useJavaMaxVersionOverride="false" javaMaxVersionOverride="" checkUpdater="false" updateExecutionMode="unattendedProgress" unattendedUpdateTitle="${i18n:updater.WindowTitle(&quot;${compiler:sys.fullName}&quot;)}">
+      <executable name="${compiler:sys.shortName}" type="1" iconSet="true" iconFile="" executableDir="" redirectStderr="false" stderrFile="error.log" stderrMode="overwrite" redirectStdout="false" stdoutFile="output.log" stdoutMode="overwrite" failOnStderrOutput="false" executableMode="1" changeWorkingDirectory="false" workingDirectory="." singleInstance="true" serviceStartType="2" serviceDependencies="" serviceDescription="" jreLocation="" executionLevel="asInvoker" checkConsoleParameter="false" globalSingleInstance="false" singleInstanceActivate="true" dpiAware="java9+">
+        <versionInfo include="false" fileVersion="" fileDescription="" legalCopyright="" internalName="" productName="" />
+      </executable>
+      <splashScreen show="false" width="0" height="0" bitmapFile="" textOverlay="false">
+        <text>
+          <statusLine x="20" y="20" text="" fontSize="8" fontColor="0,0,0" bold="false" />
+          <versionLine x="20" y="40" text="version ${compiler:sys.version}" fontSize="8" fontColor="0,0,0" bold="false" />
+        </text>
+      </splashScreen>
+      <java mainClass="jalview.bin.Launcher" mainMode="1" vmParameters="" arguments="" allowVMPassthroughParameters="true" preferredVM="" bundleRuntime="true">
+        <classPath>
+          <scanDirectory location="$$GETDOWN_DIST_DIR$$" failOnError="false" />
+        </classPath>
+        <modulePath />
+        <nativeLibraryDirectories />
+        <vmOptions />
+      </java>
+      <includedFiles />
+      <unextractableFiles />
+      <vmOptionsFile mode="template" overwriteMode="0" fileMode="644">
+        <content />
+      </vmOptionsFile>
+      <customScript mode="1" file="">
+        <content />
+      </customScript>
+      <infoPlist mode="1" file="">
+        <content />
+      </infoPlist>
+      <iconImageFiles>
+        <file path="../../resources/images/JalviewLogo_Huge.png" />
+      </iconImageFiles>
+    </launcher>
+  </launchers>
+  <installerGui installerType="1" addOnAppId="" suggestPreviousLocations="true" autoUpdateDescriptorUrl="https://www.jalview.org/install4j/updates.xml" useAutoUpdateBaseUrl="false" autoUpdateBaseUrl="">
+    <staticMembers script="" />
+    <customCode />
+    <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+      <commentFiles />
+      <customAttributes />
+    </autoUpdate>
+    <applications>
+      <application name="" id="installer" customizedId="" beanClass="com.install4j.runtime.beans.applications.InstallerApplication" enabled="true" commentSet="false" comment="" actionElevationType="none" styleId="35" fileset="" customIcnsFile="../../resources/images/jalview_logos.icns" customIcoFile="../../resources/images/jalview_logos.ico" macEntitlementsFile="" automaticLauncherIntegration="false" launchMode="startupFirstWindow" launchInNewProcess="true" launchSchedule="updateSchedule" allLaunchers="true">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.applications.InstallerApplication">
+              <void property="useCustomIcon">
+                <boolean>true</boolean>
+              </void>
+            </object>
+          </java>
+        </serializedBean>
+        <styleOverrides>
+          <styleOverride name="Customize banner image" enabled="true">
+            <group name="" id="146" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+              <serializedBean>
+                <java class="java.beans.XMLDecoder">
+                  <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                    <void property="backgroundColor">
+                      <object class="java.awt.Color">
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="borderSides">
+                      <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                        <void property="bottom">
+                          <boolean>true</boolean>
+                        </void>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBackgroundColor">
+                      <object class="java.awt.Color">
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBorder">
+                      <boolean>true</boolean>
+                    </void>
+                    <void property="imageFile">
+                      <object class="com.install4j.api.beans.ExternalFile">
+                        <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                      </object>
+                    </void>
+                    <void property="insets">
+                      <object class="java.awt.Insets">
+                        <int>5</int>
+                        <int>10</int>
+                        <int>10</int>
+                        <int>10</int>
+                      </object>
+                    </void>
+                  </object>
+                </java>
+              </serializedBean>
+              <beans />
+              <externalParametrizationPropertyNames>
+                <propertyName>imageAnchor</propertyName>
+                <propertyName>imageEdgeBackgroundColor</propertyName>
+                <propertyName>imageFile</propertyName>
+              </externalParametrizationPropertyNames>
+            </group>
+          </styleOverride>
+          <styleOverride name="Jalview" enabled="true">
+            <formComponent name="Watermark" id="352" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="5" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Jalview" externalParametrizationMode="include">
+              <serializedBean>
+                <java class="java.beans.XMLDecoder">
+                  <object class="com.install4j.runtime.beans.formcomponents.SeparatorComponent">
+                    <void property="enabledTitleText">
+                      <boolean>false</boolean>
+                    </void>
+                  </object>
+                </java>
+              </serializedBean>
+              <initScript />
+              <visibilityScript />
+              <externalParametrizationPropertyNames>
+                <propertyName>labelText</propertyName>
+              </externalParametrizationPropertyNames>
+            </formComponent>
+          </styleOverride>
+        </styleOverrides>
+        <customScript mode="1" file="">
+          <content />
+        </customScript>
+        <launcherIds />
+        <variables />
+        <startup>
+          <screen name="" id="1" customizedId="" beanClass="com.install4j.runtime.beans.screens.StartupScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.StartupScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="22" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.RequestPrivilegesAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.RequestPrivilegesAction">
+                      <void property="obtainIfAdminWin">
+                        <boolean>false</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents />
+          </screen>
+        </startup>
+        <screens>
+          <screen name="" id="2" customizedId="" beanClass="com.install4j.runtime.beans.screens.WelcomeScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.WelcomeScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides>
+              <styleOverride name="Customize banner image" enabled="true">
+                <group name="" id="145" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+                  <serializedBean>
+                    <java class="java.beans.XMLDecoder">
+                      <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                        <void property="backgroundColor">
+                          <object class="java.awt.Color">
+                            <int>255</int>
+                            <int>255</int>
+                            <int>255</int>
+                            <int>255</int>
+                          </object>
+                        </void>
+                        <void property="borderSides">
+                          <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                            <void property="bottom">
+                              <boolean>true</boolean>
+                            </void>
+                          </object>
+                        </void>
+                        <void property="imageEdgeBackgroundColor">
+                          <object class="java.awt.Color">
+                            <int>25</int>
+                            <int>143</int>
+                            <int>220</int>
+                            <int>255</int>
+                          </object>
+                        </void>
+                        <void property="imageEdgeBorder">
+                          <boolean>true</boolean>
+                        </void>
+                        <void property="imageFile">
+                          <object class="com.install4j.api.beans.ExternalFile">
+                            <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                          </object>
+                        </void>
+                        <void property="insets">
+                          <object class="java.awt.Insets">
+                            <int>5</int>
+                            <int>10</int>
+                            <int>10</int>
+                            <int>10</int>
+                          </object>
+                        </void>
+                      </object>
+                    </java>
+                  </serializedBean>
+                  <beans />
+                  <externalParametrizationPropertyNames>
+                    <propertyName>imageAnchor</propertyName>
+                    <propertyName>imageEdgeBackgroundColor</propertyName>
+                    <propertyName>imageFile</propertyName>
+                  </externalParametrizationPropertyNames>
+                </group>
+              </styleOverride>
+            </styleOverrides>
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="7" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="true" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction">
+                      <void property="excludedVariables">
+                        <array class="java.lang.String" length="1">
+                          <void index="0">
+                            <string>sys.installationDir</string>
+                          </void>
+                        </array>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="3" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:welcomeMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>!context.isConsole()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="4" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent">
+                      <void property="consoleScript">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <void property="value">
+                            <string>String message = context.getMessage("ConsoleWelcomeLabel", context.getApplicationName());
+return console.askOkCancel(message, true);
+</string>
+                          </void>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="5" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.UpdateAlertComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Update Alert" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.UpdateAlertComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>updateCheck</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+              <formComponent name="" id="6" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="20" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${i18n:ClickNext}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="8" customizedId="" beanClass="com.install4j.runtime.beans.screens.InstallationDirectoryScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.InstallationDirectoryScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition>!context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="11" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="true" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction">
+                      <void property="excludedVariables">
+                        <array class="java.lang.String" length="1">
+                          <void index="0">
+                            <string>sys.installationDir</string>
+                          </void>
+                        </array>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>context.getVariable("sys.responseFile") == null</condition>
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="9" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="25" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${i18n:SelectDirLabel(${compiler:sys.fullName})}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="10" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.InstallationDirectoryChooserComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Installation Directory Chooser" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.InstallationDirectoryChooserComponent">
+                      <void property="requestFocus">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>allowSpacesOnUnix</propertyName>
+                  <propertyName>checkFreeSpace</propertyName>
+                  <propertyName>checkWritable</propertyName>
+                  <propertyName>existingDirWarning</propertyName>
+                  <propertyName>manualEntryAllowed</propertyName>
+                  <propertyName>showFreeDiskSpace</propertyName>
+                  <propertyName>showRequiredDiskSpace</propertyName>
+                  <propertyName>standardValidation</propertyName>
+                  <propertyName>suggestAppDir</propertyName>
+                  <propertyName>validateApplicationId</propertyName>
+                  <propertyName>validationScript</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="12" customizedId="" beanClass="com.install4j.runtime.beans.screens.ComponentsScreen" enabled="false" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.ComponentsScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents>
+              <formComponent name="" id="13" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${i18n:SelectComponentsLabel2}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>!context.isConsole()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="14" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ComponentSelectorComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Installation Components" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ComponentSelectorComponent">
+                      <void property="fillVertical">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>selectionChangedScript</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="1692" customizedId="" beanClass="com.install4j.runtime.beans.screens.FileAssociationsScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.FileAssociationsScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents>
+              <formComponent name="" id="1693" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${i18n:SelectAssociationsLabel}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="1694" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.FileAssociationsComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="File Associations" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.FileAssociationsComponent">
+                      <void property="fillVertical">
+                        <boolean>true</boolean>
+                      </void>
+                      <void property="showSelectionButtons">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>showSelectionButtons</propertyName>
+                  <propertyName>selectionButtonPosition</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="15" customizedId="" beanClass="com.install4j.runtime.beans.screens.InstallationScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="true" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.InstallationScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="17" customizedId="" beanClass="com.install4j.runtime.beans.actions.InstallFilesAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="2" errorMessage="${i18n:FileCorrupted}">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.InstallFilesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="18" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateProgramGroupAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateProgramGroupAction">
+                      <void property="uninstallerMenuName">
+                        <string>${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>!context.getBooleanVariable("sys.programGroupDisabled")</condition>
+              </action>
+              <action name="" id="19" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.RegisterAddRemoveAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.RegisterAddRemoveAction">
+                      <void property="itemName">
+                        <string>${compiler:sys.fullName} ${compiler:sys.version}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="124" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" enabled="false" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                      <void property="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <void property="value">
+                            <string />
+                          </void>
+                        </object>
+                      </void>
+                      <void property="variableName">
+                        <string />
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>true</condition>
+              </action>
+              <action name="" id="134" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" enabled="false" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction">
+                      <void property="launcherId">
+                        <string>121</string>
+                      </void>
+                      <void property="vmOptions">
+                        <array class="java.lang.String" length="0" />
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="16" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ProgressComponent">
+                      <void property="initialStatusMessage">
+                        <string>${i18n:WizardPreparing}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="20" customizedId="" beanClass="com.install4j.runtime.beans.screens.FinishedScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.FinishedScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="2012" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateStartMenuEntryAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateStartMenuEntryAction">
+                      <void property="allUsers">
+                        <boolean>false</boolean>
+                      </void>
+                      <void property="entryName">
+                        <string>${compiler:sys.shortName}</string>
+                      </void>
+                      <void property="file">
+                        <object class="java.io.File">
+                          <string>${compiler:sys.shortName}</string>
+                        </object>
+                      </void>
+                      <void property="icon">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>../../resources/images/jalview_logos.ico</string>
+                        </object>
+                      </void>
+                      <void property="unixIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>../../resources/images/JalviewLogo_Huge.png</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>!context.getBooleanVariable("sys.programGroupDisabled")</condition>
+              </action>
+              <action name="" id="573" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateDesktopLinkAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="Could not make desktop link">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateDesktopLinkAction">
+                      <void property="allUsers">
+                        <boolean>false</boolean>
+                      </void>
+                      <void property="description">
+                        <string>${compiler:sys.shortName}</string>
+                      </void>
+                      <void property="file">
+                        <object class="java.io.File">
+                          <string>${compiler:sys.shortName}</string>
+                        </object>
+                      </void>
+                      <void property="name">
+                        <string>${compiler:sys.fullName}</string>
+                      </void>
+                      <void property="unixIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>../../resources/images/JalviewLogo_Huge.png</string>
+                        </object>
+                      </void>
+                      <void property="winIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>../../resources/images/jalview_logos.ico</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>context.getBooleanVariable("createDesktopLinkAction")</condition>
+              </action>
+              <action name="" id="576" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.AddToDockAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.AddToDockAction">
+                      <void property="executable">
+                        <object class="java.io.File">
+                          <string>Jalview.app</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>context.getBooleanVariable("addToDockAction")</condition>
+              </action>
+              <action name="EXTENSIONS_REPLACED_BY_GRADLE" id="1691" customizedId="EXTENSIONS" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>This action, identified by its name "EXTENSIONS_REPLACED_BY_GRADLE", will be replaced by gradle with the contents of file 'file_associations_auto_install4j.xml'.</string>
+                      </void>
+                      <void property="extension">
+                        <string>extensions_to_be_replaced_by_gradle</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="21" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="10" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:finishedMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="Add a desktop link" id="574" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                      <void property="checkboxText">
+                        <string>${i18n:CreateDesktopIcon}</string>
+                      </void>
+                      <void property="initiallySelected">
+                        <boolean>true</boolean>
+                      </void>
+                      <void property="variableName">
+                        <string>createDesktopLinkAction</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="Add an executable to the dock" id="577" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                      <void property="checkboxText">
+                        <string>${i18n:AddToDock}</string>
+                      </void>
+                      <void property="initiallySelected">
+                        <boolean>true</boolean>
+                      </void>
+                      <void property="variableName">
+                        <string>addToDockAction</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>Util.isMacOS()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+        </screens>
+      </application>
+      <application name="" id="uninstaller" customizedId="" beanClass="com.install4j.runtime.beans.applications.UninstallerApplication" enabled="true" commentSet="false" comment="" actionElevationType="none" styleId="35" fileset="" customIcnsFile="" customIcoFile="" macEntitlementsFile="" automaticLauncherIntegration="false" launchMode="startupFirstWindow" launchInNewProcess="true" launchSchedule="updateSchedule" allLaunchers="true">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.applications.UninstallerApplication">
+              <void property="customMacosExecutableName">
+                <string>${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}</string>
+              </void>
+              <void property="useCustomMacosExecutableName">
+                <boolean>true</boolean>
+              </void>
+            </object>
+          </java>
+        </serializedBean>
+        <styleOverrides>
+          <styleOverride name="Customize banner image" enabled="true">
+            <group name="" id="147" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+              <serializedBean>
+                <java class="java.beans.XMLDecoder">
+                  <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                    <void property="backgroundColor">
+                      <object class="java.awt.Color">
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="borderSides">
+                      <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                        <void property="bottom">
+                          <boolean>true</boolean>
+                        </void>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBackgroundColor">
+                      <object class="java.awt.Color">
+                        <int>192</int>
+                        <int>192</int>
+                        <int>192</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBorder">
+                      <boolean>true</boolean>
+                    </void>
+                    <void property="imageFile">
+                      <object class="com.install4j.api.beans.ExternalFile">
+                        <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                      </object>
+                    </void>
+                    <void property="insets">
+                      <object class="java.awt.Insets">
+                        <int>5</int>
+                        <int>10</int>
+                        <int>10</int>
+                        <int>10</int>
+                      </object>
+                    </void>
+                  </object>
+                </java>
+              </serializedBean>
+              <beans />
+              <externalParametrizationPropertyNames>
+                <propertyName>imageAnchor</propertyName>
+                <propertyName>imageEdgeBackgroundColor</propertyName>
+                <propertyName>imageFile</propertyName>
+              </externalParametrizationPropertyNames>
+            </group>
+          </styleOverride>
+        </styleOverrides>
+        <customScript mode="1" file="">
+          <content />
+        </customScript>
+        <launcherIds />
+        <variables />
+        <startup>
+          <screen name="" id="23" customizedId="" beanClass="com.install4j.runtime.beans.screens.StartupScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.StartupScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="33" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="34" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.RequireInstallerPrivilegesAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.RequireInstallerPrivilegesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents />
+          </screen>
+        </startup>
+        <screens>
+          <screen name="" id="24" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallWelcomeScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallWelcomeScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents>
+              <formComponent name="" id="25" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="10" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:welcomeMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>!context.isConsole()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="26" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent">
+                      <void property="consoleScript">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <void property="value">
+                            <string>String message = context.getMessage("ConfirmUninstall", context.getApplicationName());
+return console.askYesNo(message, true);
+</string>
+                          </void>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="27" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallationScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallationScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="659" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetProgressAction">
+                      <void property="progressChangeType">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.control.ProgressChangeType</class>
+                          <string>SET_INDETERMINATE</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="29" customizedId="" beanClass="com.install4j.runtime.beans.actions.UninstallFilesAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.UninstallFilesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="660" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" enabled="false" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetProgressAction">
+                      <void property="percentValue">
+                        <int>95</int>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="1525" customizedId="" beanClass="com.install4j.runtime.beans.actions.files.DeleteFileAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.files.DeleteFileAction">
+                      <void property="files">
+                        <array class="java.io.File" length="31">
+                          <void index="0">
+                            <object class="java.io.File">
+                              <string>jre</string>
+                            </object>
+                          </void>
+                          <void index="1">
+                            <object class="java.io.File">
+                              <string>jre.jar</string>
+                            </object>
+                          </void>
+                          <void index="2">
+                            <object class="java.io.File">
+                              <string>.install4j</string>
+                            </object>
+                          </void>
+                          <void index="3">
+                            <object class="java.io.File">
+                              <string>$$GETDOWN_DIST_DIR$$</string>
+                            </object>
+                          </void>
+                          <void index="4">
+                            <object class="java.io.File">
+                              <string>$$GETDOWN_ALT_DIR$$</string>
+                            </object>
+                          </void>
+                          <void index="5">
+                            <object class="java.io.File">
+                              <string>$$GETDOWN_RESOURCE_DIR$$</string>
+                            </object>
+                          </void>
+                          <void index="6">
+                            <object class="java.io.File">
+                              <string>getdown-launcher.jar</string>
+                            </object>
+                          </void>
+                          <void index="7">
+                            <object class="java.io.File">
+                              <string>getdown-launcher-old.jar</string>
+                            </object>
+                          </void>
+                          <void index="8">
+                            <object class="java.io.File">
+                              <string>getdown-launcher-new.jar</string>
+                            </object>
+                          </void>
+                          <void index="9">
+                            <object class="java.io.File">
+                              <string>*.jarv</string>
+                            </object>
+                          </void>
+                          <void index="10">
+                            <object class="java.io.File">
+                              <string>gettingdown.lock</string>
+                            </object>
+                          </void>
+                          <void index="11">
+                            <object class="java.io.File">
+                              <string>*.log</string>
+                            </object>
+                          </void>
+                          <void index="12">
+                            <object class="java.io.File">
+                              <string>*.txt</string>
+                            </object>
+                          </void>
+                          <void index="13">
+                            <object class="java.io.File">
+                              <string>*_new</string>
+                            </object>
+                          </void>
+                          <void index="14">
+                            <object class="java.io.File">
+                              <string>digest.txt</string>
+                            </object>
+                          </void>
+                          <void index="15">
+                            <object class="java.io.File">
+                              <string>digest2.txt</string>
+                            </object>
+                          </void>
+                          <void index="16">
+                            <object class="java.io.File">
+                              <string>getdown-launcher.jarv</string>
+                            </object>
+                          </void>
+                          <void index="17">
+                            <object class="java.io.File">
+                              <string>getdown-launcher-new.jarv</string>
+                            </object>
+                          </void>
+                          <void index="18">
+                            <object class="java.io.File">
+                              <string>channel_launch.jvl</string>
+                            </object>
+                          </void>
+                          <void index="19">
+                            <object class="java.io.File">
+                              <string>launcher.log</string>
+                            </object>
+                          </void>
+                          <void index="20">
+                            <object class="java.io.File">
+                              <string>proxy.txt</string>
+                            </object>
+                          </void>
+                          <void index="21">
+                            <object class="java.io.File">
+                              <string>META-INF</string>
+                            </object>
+                          </void>
+                          <void index="22">
+                            <object class="java.io.File">
+                              <string>install/getdown-launcher.jar</string>
+                            </object>
+                          </void>
+                          <void index="23">
+                            <object class="java.io.File">
+                              <string>install/getdown.txt</string>
+                            </object>
+                          </void>
+                          <void index="24">
+                            <object class="java.io.File">
+                              <string>install/build_properties</string>
+                            </object>
+                          </void>
+                          <void index="25">
+                            <object class="java.io.File">
+                              <string>build_properties</string>
+                            </object>
+                          </void>
+                          <void index="26">
+                            <object class="java.io.File">
+                              <string>install</string>
+                            </object>
+                          </void>
+                          <void index="27">
+                            <object class="java.io.File">
+                              <string>dist</string>
+                            </object>
+                          </void>
+                          <void index="28">
+                            <object class="java.io.File">
+                              <string>release</string>
+                            </object>
+                          </void>
+                          <void index="29">
+                            <object class="java.io.File">
+                              <string>alt</string>
+                            </object>
+                          </void>
+                          <void index="30">
+                            <object class="java.io.File">
+                              <string>resource</string>
+                            </object>
+                          </void>
+                        </array>
+                      </void>
+                      <void property="recursive">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="1791" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetProgressAction">
+                      <void property="percentValue">
+                        <int>100</int>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="28" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ProgressComponent">
+                      <void property="initialStatusMessage">
+                        <string>${i18n:UninstallerPreparing}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="32" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallFailureScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallFailureScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents />
+          </screen>
+          <screen name="" id="30" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallSuccessScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="41" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallSuccessScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents>
+              <formComponent name="" id="31" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="10" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:successMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+        </screens>
+      </application>
+    </applications>
+    <styles defaultStyleId="35">
+      <style name="Standard" id="35" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.styles.FormStyle" />
+          </java>
+        </serializedBean>
+        <formComponents>
+          <formComponent name="Header" id="36" customizedId="" beanClass="com.install4j.runtime.beans.styles.NestedStyleComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.NestedStyleComponent">
+                  <void property="styleId">
+                    <string>48</string>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <initScript />
+            <visibilityScript />
+            <externalParametrizationPropertyNames />
+          </formComponent>
+          <group name="Main" id="37" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                  <void property="imageEdgeAxisType">
+                    <object class="java.lang.Enum" method="valueOf">
+                      <class>com.install4j.runtime.beans.formcomponents.AxisType</class>
+                      <string>HORIZONTAL</string>
+                    </object>
+                  </void>
+                  <void property="imageFile">
+                    <object class="com.install4j.api.beans.ExternalFile">
+                      <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                    </object>
+                  </void>
+                  <void property="imageOverlap">
+                    <boolean>true</boolean>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <beans>
+              <formComponent name="" id="38" customizedId="" beanClass="com.install4j.runtime.beans.styles.ContentComponent" enabled="true" commentSet="false" comment="" insetTop="10" insetLeft="20" insetBottom="10" insetRight="20" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.ContentComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="Watermark" id="39" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="5" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Jalview" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.SeparatorComponent">
+                      <void property="enabledTitleText">
+                        <boolean>false</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>labelText</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+              <formComponent name="Footer" id="40" customizedId="" beanClass="com.install4j.runtime.beans.styles.NestedStyleComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.NestedStyleComponent">
+                      <void property="styleId">
+                        <string>52</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </beans>
+            <externalParametrizationPropertyNames />
+          </group>
+        </formComponents>
+      </style>
+      <style name="Banner" id="41" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.styles.FormStyle" />
+          </java>
+        </serializedBean>
+        <formComponents>
+          <group name="" id="42" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                  <void property="backgroundColor">
+                    <object class="java.awt.Color">
+                      <int>255</int>
+                      <int>255</int>
+                      <int>255</int>
+                      <int>255</int>
+                    </object>
+                  </void>
+                  <void property="borderSides">
+                    <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                      <void property="bottom">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </void>
+                  <void property="imageEdgeBackgroundColor">
+                    <object class="java.awt.Color">
+                      <int>25</int>
+                      <int>143</int>
+                      <int>220</int>
+                      <int>255</int>
+                    </object>
+                  </void>
+                  <void property="imageEdgeBorder">
+                    <boolean>true</boolean>
+                  </void>
+                  <void property="imageFile">
+                    <object class="com.install4j.api.beans.ExternalFile">
+                      <string>${compiler:sys.install4jHome}/resource/styles/wizard.png</string>
+                    </object>
+                  </void>
+                  <void property="insets">
+                    <object class="java.awt.Insets">
+                      <int>5</int>
+                      <int>10</int>
+                      <int>10</int>
+                      <int>10</int>
+                    </object>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <beans>
+              <formComponent name="" id="43" customizedId="" beanClass="com.install4j.runtime.beans.styles.ScreenTitleComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.ScreenTitleComponent">
+                      <void property="labelFontSizePercent">
+                        <int>130</int>
+                      </void>
+                      <void property="labelFontStyle">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.formcomponents.FontStyle</class>
+                          <string>BOLD</string>
+                        </object>
+                      </void>
+                      <void property="labelFontType">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.formcomponents.FontType</class>
+                          <string>DERIVED</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="44" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.SeparatorComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="45" customizedId="" beanClass="com.install4j.runtime.beans.styles.ContentComponent" enabled="true" commentSet="false" comment="" insetTop="10" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.ContentComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </beans>
+            <externalParametrizationPropertyNames>
+              <propertyName>imageAnchor</propertyName>
+              <propertyName>imageEdgeBackgroundColor</propertyName>
+              <propertyName>imageFile</propertyName>
+            </externalParametrizationPropertyNames>
+          </group>
+          <formComponent name="" id="46" customizedId="" beanClass="com.install4j.runtime.beans.styles.NestedStyleComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.NestedStyleComponent">
+                  <void property="styleId">
+                    <string>52</string>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <initScript />
+            <visibilityScript />
+            <externalParametrizationPropertyNames />
+          </formComponent>
+        </formComponents>
+      </style>
+      <group name="Style components" id="47" customizedId="" beanClass="com.install4j.runtime.beans.groups.StyleGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.groups.StyleGroup" />
+          </java>
+        </serializedBean>
+        <beans>
+          <style name="Standard header" id="48" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.FormStyle">
+                  <void property="fillVertical">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="standalone">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="verticalAnchor">
+                    <object class="java.lang.Enum" method="valueOf">
+                      <class>com.install4j.api.beans.Anchor</class>
+                      <string>NORTH</string>
+                    </object>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <formComponents>
+              <group name="" id="49" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize title bar" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" id="VerticalFormComponentGroup0">
+                      <void property="backgroundColor">
+                        <object class="java.awt.Color">
+                          <int>255</int>
+                          <int>255</int>
+                          <int>255</int>
+                          <int>255</int>
+                        </object>
+                      </void>
+                      <void property="borderSides">
+                        <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                          <void property="bottom">
+                            <boolean>true</boolean>
+                          </void>
+                        </object>
+                      </void>
+                      <void property="imageAnchor">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.api.beans.Anchor</class>
+                          <string>NORTHEAST</string>
+                        </object>
+                      </void>
+                      <void property="imageEdgeBorderWidth">
+                        <int>2</int>
+                      </void>
+                      <void property="imageFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>icon:${installer:sys.installerApplicationMode}_header.png</string>
+                        </object>
+                      </void>
+                      <void id="Integer0" property="cellSpacing" />
+                      <void id="Integer1" property="borderWidth" />
+                      <void property="imageInsets">
+                        <object class="java.awt.Insets">
+                          <object idref="Integer0" />
+                          <int>5</int>
+                          <object idref="Integer1" />
+                          <object idref="Integer1" />
+                        </object>
+                      </void>
+                      <void property="insets">
+                        <object class="java.awt.Insets">
+                          <object idref="Integer0" />
+                          <int>20</int>
+                          <object idref="Integer0" />
+                          <int>10</int>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <beans>
+                  <formComponent name="Title" id="50" customizedId="" beanClass="com.install4j.runtime.beans.styles.ScreenTitleComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.ScreenTitleComponent">
+                          <void property="labelFontStyle">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.runtime.beans.formcomponents.FontStyle</class>
+                              <string>BOLD</string>
+                            </object>
+                          </void>
+                          <void property="labelFontType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.runtime.beans.formcomponents.FontType</class>
+                              <string>DERIVED</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Subtitle" id="51" customizedId="" beanClass="com.install4j.runtime.beans.styles.ScreenTitleComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="8" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.ScreenTitleComponent">
+                          <void property="titleType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.runtime.beans.styles.TitleType</class>
+                              <string>SUB_TITLE</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                </beans>
+                <externalParametrizationPropertyNames>
+                  <propertyName>backgroundColor</propertyName>
+                  <propertyName>foregroundColor</propertyName>
+                  <propertyName>imageAnchor</propertyName>
+                  <propertyName>imageFile</propertyName>
+                  <propertyName>imageOverlap</propertyName>
+                </externalParametrizationPropertyNames>
+              </group>
+            </formComponents>
+          </style>
+          <style name="Standard footer" id="52" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.FormStyle">
+                  <void property="fillVertical">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="standalone">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="verticalAnchor">
+                    <object class="java.lang.Enum" method="valueOf">
+                      <class>com.install4j.api.beans.Anchor</class>
+                      <string>SOUTH</string>
+                    </object>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <formComponents>
+              <group name="" id="53" customizedId="" beanClass="com.install4j.runtime.beans.groups.HorizontalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.groups.HorizontalFormComponentGroup" id="HorizontalFormComponentGroup0">
+                      <void property="alignFirstLabel">
+                        <boolean>false</boolean>
+                      </void>
+                      <void id="Integer0" property="cellSpacing" />
+                      <void property="insets">
+                        <object class="java.awt.Insets">
+                          <int>3</int>
+                          <object idref="Integer0" />
+                          <int>8</int>
+                          <object idref="Integer0" />
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <beans>
+                  <formComponent name="" id="54" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SpringComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.formcomponents.SpringComponent" />
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Back button" id="55" customizedId="" beanClass="com.install4j.runtime.beans.styles.StandardControlButtonComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.StandardControlButtonComponent">
+                          <void property="buttonText">
+                            <string>&lt; ${i18n:ButtonBack}</string>
+                          </void>
+                          <void property="controlButtonType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.api.context.ControlButtonType</class>
+                              <string>PREVIOUS</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Next button" id="56" customizedId="" beanClass="com.install4j.runtime.beans.styles.StandardControlButtonComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.StandardControlButtonComponent">
+                          <void property="buttonText">
+                            <string>${i18n:ButtonNext} &gt;</string>
+                          </void>
+                          <void property="controlButtonType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.api.context.ControlButtonType</class>
+                              <string>NEXT</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Cancel button" id="57" customizedId="" beanClass="com.install4j.runtime.beans.styles.StandardControlButtonComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="5" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.StandardControlButtonComponent">
+                          <void property="buttonText">
+                            <string>${i18n:ButtonCancel}</string>
+                          </void>
+                          <void property="controlButtonType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.api.context.ControlButtonType</class>
+                              <string>CANCEL</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                </beans>
+                <externalParametrizationPropertyNames />
+              </group>
+            </formComponents>
+          </style>
+        </beans>
+      </group>
+    </styles>
+  </installerGui>
+  <mediaSets>
+    <linuxDeb name="Jalview .deb Package" id="153" customizedId="" mediaFileName="jalview-$$VERSION$$" installDir="/opt/${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" overwriteNeverAsConfigFiles="false" dependencies="openjdk-8-jre" bzip="true" description="Jalview Desktop" maintainerEmail="help@jalview.org" architectureSet="false" architecture="">
+      <excludedComponents>
+        <component id="1031" />
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1276" />
+        <component id="2110" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="737" />
+        <launcher id="1402" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <preInstallScript mode="1" file="">
+        <content />
+      </preInstallScript>
+      <postInstallScript mode="1" file="">
+        <content />
+      </postInstallScript>
+      <preUninstallScript mode="1" file="">
+        <content />
+      </preUninstallScript>
+      <postUninstallScript mode="1" file="">
+        <content />
+      </postUninstallScript>
+    </linuxDeb>
+    <linuxRPM name="Jalview RPM Package" id="570" customizedId="" mediaFileName="jalview-$$VERSION$$" installDir="/opt/${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" overwriteNeverAsConfigFiles="false" dependencies="java-1.8.0-openjdk" os="linux" arch="noarch">
+      <excludedComponents>
+        <component id="1031" />
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1276" />
+        <component id="2110" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="737" />
+        <launcher id="1402" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <preInstallScript mode="1" file="">
+        <content />
+      </preInstallScript>
+      <postInstallScript mode="1" file="">
+        <content />
+      </postInstallScript>
+      <preUninstallScript mode="1" file="">
+        <content />
+      </preUninstallScript>
+      <postUninstallScript mode="1" file="">
+        <content />
+      </postUninstallScript>
+    </linuxRPM>
+    <windows name="Offline Windows x64 Installer" id="743" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="64" runPostProcessor="true" postProcessor="${compiler:JSIGN_SH} $EXECUTABLE" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="$$WINDOWS_JAVA_VM_TGZ$$" manualJREEntry="true" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="~/AppData/Local" contentFilesType="1" verifyIntegrity="true">
+      <excludedComponents>
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1276" />
+        <component id="1881" />
+        <component id="2110" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="1402" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+        <entry location=".i4j_fileset_1873" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+    </windows>
+    <windows name="Network Windows x64 Installer" id="1862" customizedId="" mediaFileName="${compiler:sys.shortName}-NETWORK_${compiler:sys.platform}_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="64" runPostProcessor="true" postProcessor="${compiler:JSIGN_SH} $EXECUTABLE" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="$$WINDOWS_JAVA_VM_TGZ$$" manualJREEntry="true" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="~/AppData/Local" contentFilesType="1" verifyIntegrity="true">
+      <excludedComponents>
+        <component id="1031" />
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1881" />
+        <component id="2110" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_1873" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+    </windows>
+    <macosArchive name="Offline macOS Disk Image" id="878" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}-app_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" archiveType="dmg" volumeName="${compiler:sys.shortName} Installer" launcherId="737">
+      <excludedComponents>
+        <component id="1156" />
+        <component id="1276" />
+        <component id="1881" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+        <entry location=".i4j_fileset_1873" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <topLevelFiles>
+        <symlink name="&quot; &quot;" target="/Applications" />
+        <file name=".background/jalview_dmg_background.png" file="./jalview_dmg_background.png" />
+        <file name=".DS_Store" file="./DS_Store" />
+        <symlink name="Jalview.app/Contents/Resources/app/${compiler:JRE_DIR}/Contents/MacOS/libjli.dylib" target="../Home/lib/jli/libjli.dylib" />
+        <file name="Jalview.app/Contents/Resources/Jalview-File.icns" file="./Jalview-File.icns" />
+        <file name="Jalview.app/Contents/Resources/Jalview-Version-Locator.icns" file="Jalview-Version-Locator.icns" />
+        <symlink name="Uninstall Old Jalview (optional).app" target="Jalview.app/Contents/Resources/app/Uninstall Old Jalview.app" />
+      </topLevelFiles>
+    </macosArchive>
+    <macosArchive name="Network macOS Disk Image" id="1274" customizedId="" mediaFileName="${compiler:sys.shortName}-NETWORK_${compiler:sys.platform}-app_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" archiveType="dmg" volumeName="${compiler:sys.shortName} Installer" launcherId="1402">
+      <excludedComponents>
+        <component id="1031" />
+        <component id="1156" />
+        <component id="1881" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+        <entry location=".i4j_fileset_1873" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <topLevelFiles>
+        <symlink name="&quot; &quot;" target="/Applications" />
+        <file name=".background/jalview_dmg_background.png" file="./jalview_dmg_background.png" />
+        <file name=".DS_Store" file="./DS_Store" />
+        <symlink name="Jalview.app/Contents/Resources/app/${compiler:JRE_DIR}/Contents/MacOS/libjli.dylib" target="../Home/lib/jli/libjli.dylib" />
+        <file name="Jalview.app/Contents/Resources/Jalview-Version-Locator.icns" file="Jalview-Version-Locator.icns" />
+        <file name="Jalview.app/Contents/Resources/Jalview-File.icns" file="Jalview-File.icns" />
+        <symlink name="Uninstall Old Jalview (optional).app" target="Jalview.app/Contents/Resources/app/Uninstall Old Jalview.app" />
+      </topLevelFiles>
+    </macosArchive>
+    <unixInstaller name="Offline Unix Shell Installer" id="1595" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}_installer_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="~/opt/" contentFilesType="1">
+      <excludedComponents>
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1276" />
+        <component id="1881" />
+        <component id="2110" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="1402" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+        <entry location=".i4j_fileset_1873" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <installerScript mode="1" file="">
+        <content />
+      </installerScript>
+    </unixInstaller>
+    <unixInstaller name="Network Unix Shell Installer" id="1867" customizedId="" mediaFileName="${compiler:sys.shortName}-NETWORK_${compiler:sys.platform}_installer_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="~/opt/" contentFilesType="1">
+      <excludedComponents>
+        <component id="1031" />
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1881" />
+        <component id="2110" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+        <entry location=".i4j_fileset_1873" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <installerScript mode="1" file="">
+        <content />
+      </installerScript>
+    </unixInstaller>
+    <unixArchive name="Offline Unix .tar.gz Archive" id="1596" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}_archive_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false">
+      <excludedComponents>
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1276" />
+        <component id="1881" />
+        <component id="2110" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="1402" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+        <entry location=".i4j_fileset_1873" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+    </unixArchive>
+    <unixArchive name="Network Unix .tar.gz Archive" id="1871" customizedId="" mediaFileName="${compiler:sys.shortName}-NETWORK_${compiler:sys.platform}_archive_${compiler:sys.version}-j$$JAVA_INTEGER_VERSION$$" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false">
+      <excludedComponents>
+        <component id="1031" />
+        <component id="1155" />
+        <component id="1156" />
+        <component id="1881" />
+        <component id="2110" />
+      </excludedComponents>
+      <includedDownloadableComponents />
+      <excludedLaunchers />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+        <entry location=".i4j_fileset_1873" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+    </unixArchive>
+  </mediaSets>
+  <buildIds buildAll="true">
+    <mediaSet refId="153" />
+    <mediaSet refId="570" />
+    <mediaSet refId="743" />
+    <mediaSet refId="878" />
+    <mediaSet refId="1274" />
+    <mediaSet refId="1595" />
+    <mediaSet refId="1596" />
+    <mediaSet refId="1862" />
+    <mediaSet refId="1867" />
+    <mediaSet refId="1871" />
+  </buildIds>
+  <buildOptions verbose="false" faster="false" disableSigning="false" disableJreBundling="false" debug="false" />
+</install4j>
diff --git a/utils/install4j/jalview_dmg_background.png b/utils/install4j/jalview_dmg_background.png
new file mode 100644 (file)
index 0000000..cfd924d
Binary files /dev/null and b/utils/install4j/jalview_dmg_background.png differ
diff --git a/utils/install4j/jalview_getdown.install4j b/utils/install4j/jalview_getdown.install4j
new file mode 100644 (file)
index 0000000..66bd257
--- /dev/null
@@ -0,0 +1,1774 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<install4j version="7.0.9" transformSequenceNumber="7">
+  <directoryPresets config="libjli.dylib" />
+  <application name="Jalview" distributionSourceDir="" applicationId="6595-2347-1923-0725" mediaDir="../../build/install4j" mediaFilePattern="${compiler:sys.shortName}_${compiler:sys.platform}_${compiler:sys.version}" compression="6" lzmaCompression="true" pack200Compression="false" excludeSignedFromPacking="true" commonExternalFiles="false" createMd5Sums="true" shrinkRuntime="true" shortName="Jalview" publisher="University of Dundee" publisherWeb="http://www.jalview.org/" version="DEVELOPMENT" allPathsRelative="true" backupOnSave="false" autoSave="false" convertDotsToUnderscores="true" macSignature="????" macVolumeId="5aac4968c304f65" javaMinVersion="1.8" javaMaxVersion="" allowBetaVM="true" jdkMode="jdk" jdkName="JDK 11.0">
+    <languages skipLanguageSelection="false" languageSelectionInPrincipalLanguage="false">
+      <principalLanguage id="en" customLocalizationFile="" />
+      <additionalLanguages />
+    </languages>
+    <searchSequence>
+      <directory location="java_vm" />
+    </searchSequence>
+    <variables />
+    <mergedProjects />
+    <codeSigning macEnabled="false" macPkcs12File="" windowsEnabled="false" windowsKeySource="pkcs12" windowsPvkFile="" windowsSpcFile="" windowsPkcs12File="" windowsPkcs11Library="" windowsPkcs11Slot="">
+      <windowsKeystoreIdentifier issuer="" serial="" subject="" />
+      <windowsPkcs11Identifier issuer="" serial="" subject="" />
+    </codeSigning>
+  </application>
+  <files keepModificationTimes="false" missingFilesStrategy="warn" globalExcludeSuffixes="" defaultOverwriteMode="4" defaultUninstallMode="2" launcherOverwriteMode="3" defaultFileMode="644" defaultDirMode="755">
+    <filesets>
+      <fileset name="Full file set" id="734" customizedId="" />
+      <fileset name="Mac OS X JRE" id="880" customizedId="" />
+      <fileset name="Windows JRE" id="882" customizedId="" />
+      <fileset name="Full Files With MacOS JRE" id="1015" customizedId="" />
+      <fileset name="Full Files With Windows JRE" id="1017" customizedId="" />
+    </filesets>
+    <roots>
+      <root id="735" fileset="734" location="" />
+      <root id="881" fileset="880" location="" />
+      <root id="883" fileset="882" location="" />
+      <root id="1016" fileset="1015" location="" />
+      <root id="1018" fileset="1017" location="" />
+    </roots>
+    <mountPoints>
+      <mountPoint id="454" root="" location="" mode="755" />
+      <mountPoint id="736" root="735" location="" mode="755" />
+      <mountPoint id="884" root="881" location="" mode="755" />
+      <mountPoint id="885" root="883" location="" mode="755" />
+      <mountPoint id="1019" root="1016" location="" mode="755" />
+      <mountPoint id="1020" root="1018" location="" mode="755" />
+    </mountPoints>
+    <entries>
+      <dirEntry mountPoint="454" file="../../getdown/files" overwriteMode="4" shared="false" fileMode="644" uninstallMode="0" overrideFileMode="false" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="direct" subDirectory="files" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="736" file="../../getdown/website" overwriteMode="4" shared="false" fileMode="644" uninstallMode="0" overrideFileMode="false" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="direct" subDirectory="files" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="884" file="./jres/macosx-amd64-1.8.0_202/java_vm" overwriteMode="4" shared="false" fileMode="755" uninstallMode="0" overrideFileMode="true" overrideOverwriteMode="false" overrideUninstallMode="true" entryMode="subdir" subDirectory="java_vm" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="885" file="./jres/windows-amd64-1.8.0_202/java_vm" overwriteMode="4" shared="false" fileMode="755" uninstallMode="0" overrideFileMode="true" overrideOverwriteMode="false" overrideUninstallMode="true" entryMode="subdir" subDirectory="java_vm" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="1019" file="../../getdown/website" overwriteMode="4" shared="false" fileMode="644" uninstallMode="0" overrideFileMode="false" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="direct" subDirectory="files" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="1019" file="./jres/macosx-amd64-1.8.0_202/java_vm" overwriteMode="4" shared="false" fileMode="755" uninstallMode="0" overrideFileMode="true" overrideOverwriteMode="false" overrideUninstallMode="true" entryMode="subdir" subDirectory="java_vm" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="1020" file="../../getdown/website" overwriteMode="4" shared="false" fileMode="644" uninstallMode="0" overrideFileMode="false" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="direct" subDirectory="files" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+      <dirEntry mountPoint="1020" file="./jres/windows-amd64-1.8.0_202/java_vm" overwriteMode="4" shared="false" fileMode="755" uninstallMode="0" overrideFileMode="true" overrideOverwriteMode="false" overrideUninstallMode="true" entryMode="subdir" subDirectory="java_vm" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+        <exclude />
+      </dirEntry>
+    </entries>
+    <components>
+      <component name="jalview_getdown" id="1031" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+        <description />
+        <include all="false">
+          <entry location=".i4j_fileset_734" fileType="regular" />
+          <entry location=".i4j_fileset_880" fileType="regular" />
+          <entry location=".i4j_fileset_882" fileType="regular" />
+        </include>
+        <dependencies />
+      </component>
+    </components>
+  </files>
+  <launchers>
+    <launcher name="Jalview Launcher" id="121" customizedId="" external="false" excludeFromMenu="false" unixMode="755" unixAutoStart="true" menuName="${compiler:sys.shortName}-${compiler:sys.version}" icnsFile="../../resources/images/jalview_logos.icns" customMacBundleIdentifier="false" macBundleIdentifier="" swtApp="false" fileset="" macBundleBinary="JavaApplicationStub" addMacEntitlements="false" macEntitlementsFile="" useCustomMacosExecutableName="true" customMacosExecutableName="${compiler:sys.shortName}" useJavaMinVersionOverride="false" javaMinVersionOverride="" useJavaMaxVersionOverride="false" javaMaxVersionOverride="" checkUpdater="false" updateExecutionMode="unattendedProgress" unattendedUpdateTitle="${i18n:updater.WindowTitle(&quot;${compiler:sys.fullName}&quot;)}">
+      <executable name="Jalview" type="1" iconSet="true" iconFile="../../resources/images/jalview_logos.ico" executableDir="." redirectStderr="true" stderrFile="error.log" stderrMode="overwrite" redirectStdout="true" stdoutFile="output.log" stdoutMode="overwrite" failOnStderrOutput="true" executableMode="1" changeWorkingDirectory="true" workingDirectory="." singleInstance="true" serviceStartType="2" serviceDependencies="" serviceDescription="" jreLocation="" executionLevel="asInvoker" checkConsoleParameter="true" globalSingleInstance="false" singleInstanceActivate="true" dpiAware="java9+">
+        <versionInfo include="false" fileVersion="" fileDescription="" legalCopyright="" internalName="" productName="" />
+      </executable>
+      <splashScreen show="false" width="640" height="480" bitmapFile="../../resources/images/jalview_logo_background_fade-640x480.png" textOverlay="true">
+        <text>
+          <statusLine x="85" y="81" text="${compiler:sys.shortName}" fontSize="18" fontColor="0,0,0" bold="false" />
+          <versionLine x="85" y="109" text="version ${compiler:sys.version}" fontSize="8" fontColor="0,0,0" bold="false" />
+        </text>
+      </splashScreen>
+      <java mainClass="com.threerings.getdown.launcher.GetdownApp" mainMode="1" vmParameters="" arguments="." allowVMPassthroughParameters="true" preferredVM="" bundleRuntime="true">
+        <classPath>
+          <archive location="getdown-launcher.jar" failOnError="false" />
+          <archive location="commons-compress-1.18.jar" failOnError="false" />
+        </classPath>
+        <modulePath />
+        <nativeLibraryDirectories />
+        <vmOptions />
+      </java>
+      <includedFiles />
+      <unextractableFiles />
+      <vmOptionsFile mode="template" overwriteMode="0" fileMode="644">
+        <content />
+      </vmOptionsFile>
+      <customScript mode="1" file="">
+        <content />
+      </customScript>
+      <infoPlist mode="1" file="">
+        <content />
+      </infoPlist>
+      <iconImageFiles>
+        <file path="../../resources/images/Jalview_Logo.png" />
+      </iconImageFiles>
+    </launcher>
+    <launcher name="MacOS Offline Jalview Launcher" id="737" customizedId="" external="false" excludeFromMenu="false" unixMode="755" unixAutoStart="true" menuName="${compiler:sys.shortName}-${compiler:sys.version}" icnsFile="../../resources/images/jalview_logos.icns" customMacBundleIdentifier="false" macBundleIdentifier="" swtApp="false" fileset="1015" macBundleBinary="JavaApplicationStub" addMacEntitlements="false" macEntitlementsFile="" useCustomMacosExecutableName="true" customMacosExecutableName="${compiler:sys.shortName}" useJavaMinVersionOverride="false" javaMinVersionOverride="" useJavaMaxVersionOverride="false" javaMaxVersionOverride="" checkUpdater="false" updateExecutionMode="unattendedProgress" unattendedUpdateTitle="${i18n:updater.WindowTitle(&quot;${compiler:sys.fullName}&quot;)}">
+      <executable name="Jalview" type="1" iconSet="true" iconFile="../../resources/images/jalview_logos.ico" executableDir="." redirectStderr="true" stderrFile="error.log" stderrMode="overwrite" redirectStdout="true" stdoutFile="output.log" stdoutMode="overwrite" failOnStderrOutput="true" executableMode="1" changeWorkingDirectory="true" workingDirectory="." singleInstance="true" serviceStartType="2" serviceDependencies="" serviceDescription="" jreLocation="" executionLevel="asInvoker" checkConsoleParameter="true" globalSingleInstance="false" singleInstanceActivate="true" dpiAware="java9+">
+        <versionInfo include="false" fileVersion="" fileDescription="" legalCopyright="" internalName="" productName="" />
+      </executable>
+      <splashScreen show="false" width="640" height="480" bitmapFile="../../resources/images/jalview_logo_background_fade-640x480.png" textOverlay="true">
+        <text>
+          <statusLine x="85" y="81" text="${compiler:sys.shortName}" fontSize="18" fontColor="0,0,0" bold="false" />
+          <versionLine x="85" y="109" text="version ${compiler:sys.version}" fontSize="8" fontColor="0,0,0" bold="false" />
+        </text>
+      </splashScreen>
+      <java mainClass="com.threerings.getdown.launcher.GetdownApp" mainMode="1" vmParameters="" arguments="." allowVMPassthroughParameters="true" preferredVM="" bundleRuntime="true">
+        <classPath>
+          <archive location="getdown-launcher.jar" failOnError="false" />
+          <archive location="commons-compress-1.18.jar" failOnError="false" />
+        </classPath>
+        <modulePath />
+        <nativeLibraryDirectories />
+        <vmOptions />
+      </java>
+      <includedFiles />
+      <unextractableFiles />
+      <vmOptionsFile mode="template" overwriteMode="0" fileMode="644">
+        <content />
+      </vmOptionsFile>
+      <customScript mode="1" file="">
+        <content />
+      </customScript>
+      <infoPlist mode="1" file="">
+        <content />
+      </infoPlist>
+      <iconImageFiles>
+        <file path="../../resources/images/Jalview_Logo.png" />
+      </iconImageFiles>
+    </launcher>
+    <launcher name="Windows Offline Jalview Launcher" id="1022" customizedId="" external="false" excludeFromMenu="false" unixMode="755" unixAutoStart="true" menuName="${compiler:sys.shortName}-${compiler:sys.version}" icnsFile="../../resources/images/jalview_logos.icns" customMacBundleIdentifier="false" macBundleIdentifier="" swtApp="false" fileset="1017" macBundleBinary="JavaApplicationStub" addMacEntitlements="false" macEntitlementsFile="" useCustomMacosExecutableName="true" customMacosExecutableName="${compiler:sys.shortName}" useJavaMinVersionOverride="false" javaMinVersionOverride="" useJavaMaxVersionOverride="false" javaMaxVersionOverride="" checkUpdater="false" updateExecutionMode="unattendedProgress" unattendedUpdateTitle="${i18n:updater.WindowTitle(&quot;${compiler:sys.fullName}&quot;)}">
+      <executable name="Jalview" type="1" iconSet="true" iconFile="../../resources/images/jalview_logos.ico" executableDir="." redirectStderr="true" stderrFile="error.log" stderrMode="overwrite" redirectStdout="true" stdoutFile="output.log" stdoutMode="overwrite" failOnStderrOutput="true" executableMode="1" changeWorkingDirectory="true" workingDirectory="." singleInstance="true" serviceStartType="2" serviceDependencies="" serviceDescription="" jreLocation="" executionLevel="asInvoker" checkConsoleParameter="true" globalSingleInstance="false" singleInstanceActivate="true" dpiAware="java9+">
+        <versionInfo include="false" fileVersion="" fileDescription="" legalCopyright="" internalName="" productName="" />
+      </executable>
+      <splashScreen show="false" width="640" height="480" bitmapFile="../../resources/images/jalview_logo_background_fade-640x480.png" textOverlay="true">
+        <text>
+          <statusLine x="85" y="81" text="${compiler:sys.shortName}" fontSize="18" fontColor="0,0,0" bold="false" />
+          <versionLine x="85" y="109" text="version ${compiler:sys.version}" fontSize="8" fontColor="0,0,0" bold="false" />
+        </text>
+      </splashScreen>
+      <java mainClass="com.threerings.getdown.launcher.GetdownApp" mainMode="1" vmParameters="" arguments="." allowVMPassthroughParameters="true" preferredVM="" bundleRuntime="true">
+        <classPath>
+          <archive location="getdown-launcher.jar" failOnError="false" />
+          <archive location="commons-compress-1.18.jar" failOnError="false" />
+        </classPath>
+        <modulePath />
+        <nativeLibraryDirectories />
+        <vmOptions />
+      </java>
+      <includedFiles />
+      <unextractableFiles />
+      <vmOptionsFile mode="template" overwriteMode="0" fileMode="644">
+        <content />
+      </vmOptionsFile>
+      <customScript mode="1" file="">
+        <content />
+      </customScript>
+      <infoPlist mode="1" file="">
+        <content />
+      </infoPlist>
+      <iconImageFiles>
+        <file path="../../resources/images/Jalview_Logo.png" />
+      </iconImageFiles>
+    </launcher>
+  </launchers>
+  <installerGui installerType="1" addOnAppId="" suggestPreviousLocations="true" autoUpdateDescriptorUrl="" useAutoUpdateBaseUrl="false" autoUpdateBaseUrl="">
+    <staticMembers script="" />
+    <customCode />
+    <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+      <commentFiles />
+      <customAttributes />
+    </autoUpdate>
+    <applications>
+      <application name="" id="installer" customizedId="" beanClass="com.install4j.runtime.beans.applications.InstallerApplication" enabled="true" commentSet="false" comment="" actionElevationType="none" styleId="35" fileset="" customIcnsFile="../../resources/images/jalview_logos.icns" customIcoFile="../../resources/images/jalview_logos.ico" macEntitlementsFile="" automaticLauncherIntegration="false" launchMode="startupFirstWindow" launchInNewProcess="true" launchSchedule="updateSchedule" allLaunchers="true">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.applications.InstallerApplication">
+              <void property="useCustomIcon">
+                <boolean>true</boolean>
+              </void>
+            </object>
+          </java>
+        </serializedBean>
+        <styleOverrides>
+          <styleOverride name="Customize banner image" enabled="true">
+            <group name="" id="146" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+              <serializedBean>
+                <java class="java.beans.XMLDecoder">
+                  <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                    <void property="backgroundColor">
+                      <object class="java.awt.Color">
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="borderSides">
+                      <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                        <void property="bottom">
+                          <boolean>true</boolean>
+                        </void>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBackgroundColor">
+                      <object class="java.awt.Color">
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBorder">
+                      <boolean>true</boolean>
+                    </void>
+                    <void property="imageFile">
+                      <object class="com.install4j.api.beans.ExternalFile">
+                        <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                      </object>
+                    </void>
+                    <void property="insets">
+                      <object class="java.awt.Insets">
+                        <int>5</int>
+                        <int>10</int>
+                        <int>10</int>
+                        <int>10</int>
+                      </object>
+                    </void>
+                  </object>
+                </java>
+              </serializedBean>
+              <beans />
+              <externalParametrizationPropertyNames>
+                <propertyName>imageAnchor</propertyName>
+                <propertyName>imageEdgeBackgroundColor</propertyName>
+                <propertyName>imageFile</propertyName>
+              </externalParametrizationPropertyNames>
+            </group>
+          </styleOverride>
+          <styleOverride name="Jalview" enabled="true">
+            <formComponent name="Watermark" id="352" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="5" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Jalview" externalParametrizationMode="include">
+              <serializedBean>
+                <java class="java.beans.XMLDecoder">
+                  <object class="com.install4j.runtime.beans.formcomponents.SeparatorComponent">
+                    <void property="enabledTitleText">
+                      <boolean>false</boolean>
+                    </void>
+                    <void property="labelText">
+                      <string>install4j</string>
+                    </void>
+                  </object>
+                </java>
+              </serializedBean>
+              <initScript />
+              <visibilityScript />
+              <externalParametrizationPropertyNames>
+                <propertyName>labelText</propertyName>
+              </externalParametrizationPropertyNames>
+            </formComponent>
+          </styleOverride>
+        </styleOverrides>
+        <customScript mode="1" file="">
+          <content />
+        </customScript>
+        <launcherIds />
+        <variables />
+        <startup>
+          <screen name="" id="1" customizedId="" beanClass="com.install4j.runtime.beans.screens.StartupScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.StartupScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="22" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.RequestPrivilegesAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.RequestPrivilegesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents />
+          </screen>
+        </startup>
+        <screens>
+          <screen name="" id="2" customizedId="" beanClass="com.install4j.runtime.beans.screens.WelcomeScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.WelcomeScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides>
+              <styleOverride name="Customize banner image" enabled="true">
+                <group name="" id="145" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+                  <serializedBean>
+                    <java class="java.beans.XMLDecoder">
+                      <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                        <void property="backgroundColor">
+                          <object class="java.awt.Color">
+                            <int>255</int>
+                            <int>255</int>
+                            <int>255</int>
+                            <int>255</int>
+                          </object>
+                        </void>
+                        <void property="borderSides">
+                          <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                            <void property="bottom">
+                              <boolean>true</boolean>
+                            </void>
+                          </object>
+                        </void>
+                        <void property="imageEdgeBackgroundColor">
+                          <object class="java.awt.Color">
+                            <int>25</int>
+                            <int>143</int>
+                            <int>220</int>
+                            <int>255</int>
+                          </object>
+                        </void>
+                        <void property="imageEdgeBorder">
+                          <boolean>true</boolean>
+                        </void>
+                        <void property="imageFile">
+                          <object class="com.install4j.api.beans.ExternalFile">
+                            <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                          </object>
+                        </void>
+                        <void property="insets">
+                          <object class="java.awt.Insets">
+                            <int>5</int>
+                            <int>10</int>
+                            <int>10</int>
+                            <int>10</int>
+                          </object>
+                        </void>
+                      </object>
+                    </java>
+                  </serializedBean>
+                  <beans />
+                  <externalParametrizationPropertyNames>
+                    <propertyName>imageAnchor</propertyName>
+                    <propertyName>imageEdgeBackgroundColor</propertyName>
+                    <propertyName>imageFile</propertyName>
+                  </externalParametrizationPropertyNames>
+                </group>
+              </styleOverride>
+            </styleOverrides>
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="7" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="true" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction">
+                      <void property="excludedVariables">
+                        <array class="java.lang.String" length="1">
+                          <void index="0">
+                            <string>sys.installationDir</string>
+                          </void>
+                        </array>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="3" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:welcomeMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>!context.isConsole()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="4" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent">
+                      <void property="consoleScript">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <void property="value">
+                            <string>String message = context.getMessage("ConsoleWelcomeLabel", context.getApplicationName());
+return console.askOkCancel(message, true);
+</string>
+                          </void>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="5" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.UpdateAlertComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Update Alert" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.UpdateAlertComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>updateCheck</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+              <formComponent name="" id="6" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="20" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${i18n:ClickNext}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="8" customizedId="" beanClass="com.install4j.runtime.beans.screens.InstallationDirectoryScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.InstallationDirectoryScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition>!context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="11" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="true" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction">
+                      <void property="excludedVariables">
+                        <array class="java.lang.String" length="1">
+                          <void index="0">
+                            <string>sys.installationDir</string>
+                          </void>
+                        </array>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>context.getVariable("sys.responseFile") == null</condition>
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="9" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="25" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${i18n:SelectDirLabel(${compiler:sys.fullName})}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="10" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.InstallationDirectoryChooserComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Installation Directory Chooser" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.InstallationDirectoryChooserComponent">
+                      <void property="requestFocus">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>suggestAppDir</propertyName>
+                  <propertyName>validateApplicationId</propertyName>
+                  <propertyName>existingDirWarning</propertyName>
+                  <propertyName>checkWritable</propertyName>
+                  <propertyName>manualEntryAllowed</propertyName>
+                  <propertyName>checkFreeSpace</propertyName>
+                  <propertyName>showRequiredDiskSpace</propertyName>
+                  <propertyName>showFreeDiskSpace</propertyName>
+                  <propertyName>allowSpacesOnUnix</propertyName>
+                  <propertyName>validationScript</propertyName>
+                  <propertyName>standardValidation</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="12" customizedId="" beanClass="com.install4j.runtime.beans.screens.ComponentsScreen" enabled="false" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.ComponentsScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents>
+              <formComponent name="" id="13" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${i18n:SelectComponentsLabel2}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>!context.isConsole()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="14" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ComponentSelectorComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Installation Components" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ComponentSelectorComponent">
+                      <void property="fillVertical">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>selectionChangedScript</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="15" customizedId="" beanClass="com.install4j.runtime.beans.screens.InstallationScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="true" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.InstallationScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="17" customizedId="" beanClass="com.install4j.runtime.beans.actions.InstallFilesAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="2" errorMessage="${i18n:FileCorrupted}">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.InstallFilesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="18" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateProgramGroupAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateProgramGroupAction">
+                      <void property="uninstallerMenuName">
+                        <string>${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>!context.getBooleanVariable("sys.programGroupDisabled")</condition>
+              </action>
+              <action name="" id="19" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.RegisterAddRemoveAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.RegisterAddRemoveAction">
+                      <void property="itemName">
+                        <string>${compiler:sys.fullName} ${compiler:sys.version}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="124" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" enabled="false" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                      <void property="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <void property="value">
+                            <string />
+                          </void>
+                        </object>
+                      </void>
+                      <void property="variableName">
+                        <string />
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>true</condition>
+              </action>
+              <action name="" id="134" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction" enabled="false" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.AddVmOptionsAction">
+                      <void property="launcherId">
+                        <string>121</string>
+                      </void>
+                      <void property="vmOptions">
+                        <array class="java.lang.String" length="0" />
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="16" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ProgressComponent">
+                      <void property="initialStatusMessage">
+                        <string>${i18n:WizardPreparing}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="20" customizedId="" beanClass="com.install4j.runtime.beans.screens.FinishedScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.FinishedScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="573" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateDesktopLinkAction" enabled="false" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateDesktopLinkAction">
+                      <void property="name">
+                        <string>${compiler:sys.fullName}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition>context.getBooleanVariable("createDesktopLinkAction")</condition>
+              </action>
+              <action name="" id="575" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.AddStartupItemAction" enabled="false" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.AddStartupItemAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="576" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.AddToDockAction" enabled="false" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.AddToDockAction" />
+                  </java>
+                </serializedBean>
+                <condition>context.getBooleanVariable("addToDockAction")</condition>
+              </action>
+              <action name="" id="578" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction">
+                      <void property="description">
+                        <string>Jalview Project File</string>
+                      </void>
+                      <void property="extension">
+                        <string>jvp</string>
+                      </void>
+                      <void property="launcherId">
+                        <string>121</string>
+                      </void>
+                      <void property="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>../../resources/images/file.png</string>
+                        </object>
+                      </void>
+                      <void property="macRole">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.desktop.MacAssociationRole</class>
+                          <string>EDITOR</string>
+                        </object>
+                      </void>
+                      <void property="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>../../resources/images/file.png</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="21" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="10" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:finishedMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="Add a desktop link" id="574" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                      <void property="checkboxText">
+                        <string>${i18n:CreateDesktopIcon}</string>
+                      </void>
+                      <void property="initiallySelected">
+                        <boolean>true</boolean>
+                      </void>
+                      <void property="variableName">
+                        <string>createDesktopLinkAction</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="Add an executable to the dock" id="577" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                      <void property="checkboxText">
+                        <string>${i18n:AddToDock}</string>
+                      </void>
+                      <void property="initiallySelected">
+                        <boolean>true</boolean>
+                      </void>
+                      <void property="variableName">
+                        <string>addToDockAction</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>Util.isMacOS()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+        </screens>
+      </application>
+      <application name="" id="uninstaller" customizedId="" beanClass="com.install4j.runtime.beans.applications.UninstallerApplication" enabled="true" commentSet="false" comment="" actionElevationType="none" styleId="41" fileset="" customIcnsFile="" customIcoFile="" macEntitlementsFile="" automaticLauncherIntegration="false" launchMode="startupFirstWindow" launchInNewProcess="true" launchSchedule="updateSchedule" allLaunchers="true">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.applications.UninstallerApplication">
+              <void property="customMacosExecutableName">
+                <string>${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}</string>
+              </void>
+              <void property="useCustomMacosExecutableName">
+                <boolean>true</boolean>
+              </void>
+            </object>
+          </java>
+        </serializedBean>
+        <styleOverrides>
+          <styleOverride name="Customize banner image" enabled="true">
+            <group name="" id="147" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+              <serializedBean>
+                <java class="java.beans.XMLDecoder">
+                  <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                    <void property="backgroundColor">
+                      <object class="java.awt.Color">
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="borderSides">
+                      <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                        <void property="bottom">
+                          <boolean>true</boolean>
+                        </void>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBackgroundColor">
+                      <object class="java.awt.Color">
+                        <int>192</int>
+                        <int>192</int>
+                        <int>192</int>
+                        <int>255</int>
+                      </object>
+                    </void>
+                    <void property="imageEdgeBorder">
+                      <boolean>true</boolean>
+                    </void>
+                    <void property="imageFile">
+                      <object class="com.install4j.api.beans.ExternalFile">
+                        <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                      </object>
+                    </void>
+                    <void property="insets">
+                      <object class="java.awt.Insets">
+                        <int>5</int>
+                        <int>10</int>
+                        <int>10</int>
+                        <int>10</int>
+                      </object>
+                    </void>
+                  </object>
+                </java>
+              </serializedBean>
+              <beans />
+              <externalParametrizationPropertyNames>
+                <propertyName>imageAnchor</propertyName>
+                <propertyName>imageEdgeBackgroundColor</propertyName>
+                <propertyName>imageFile</propertyName>
+              </externalParametrizationPropertyNames>
+            </group>
+          </styleOverride>
+        </styleOverrides>
+        <customScript mode="1" file="">
+          <content />
+        </customScript>
+        <launcherIds />
+        <variables />
+        <startup>
+          <screen name="" id="23" customizedId="" beanClass="com.install4j.runtime.beans.screens.StartupScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.StartupScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="33" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="34" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.RequireInstallerPrivilegesAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.misc.RequireInstallerPrivilegesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents />
+          </screen>
+        </startup>
+        <screens>
+          <screen name="" id="24" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallWelcomeScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="41" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallWelcomeScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents>
+              <formComponent name="" id="25" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="10" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:welcomeMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript>!context.isConsole()</visibilityScript>
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="26" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent">
+                      <void property="consoleScript">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <void property="value">
+                            <string>String message = context.getMessage("ConfirmUninstall", context.getApplicationName());
+return console.askYesNo(message, true);
+</string>
+                          </void>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="27" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallationScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallationScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions>
+              <action name="" id="659" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetProgressAction">
+                      <void property="progressChangeType">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.actions.control.ProgressChangeType</class>
+                          <string>SET_INDETERMINATE</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="29" customizedId="" beanClass="com.install4j.runtime.beans.actions.UninstallFilesAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.UninstallFilesAction" />
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+              <action name="" id="660" customizedId="" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" rollbackBarrierExitCode="0" multiExec="false" failureStrategy="1" errorMessage="">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.actions.control.SetProgressAction">
+                      <void property="percentValue">
+                        <int>100</int>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <condition />
+              </action>
+            </actions>
+            <formComponents>
+              <formComponent name="" id="28" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.ProgressComponent">
+                      <void property="initialStatusMessage">
+                        <string>${i18n:UninstallerPreparing}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="" id="32" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallFailureScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallFailureScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents />
+          </screen>
+          <screen name="" id="30" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallSuccessScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" styleId="41" rollbackBarrier="false" rollbackBarrierExitCode="0" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.screens.UninstallSuccessScreen" />
+              </java>
+            </serializedBean>
+            <styleOverrides />
+            <condition />
+            <validation />
+            <preActivation />
+            <postActivation />
+            <actions />
+            <formComponents>
+              <formComponent name="" id="31" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="10" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                      <void property="labelText">
+                        <string>${form:successMessage}</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </formComponents>
+          </screen>
+        </screens>
+      </application>
+    </applications>
+    <styles defaultStyleId="35">
+      <style name="Standard" id="35" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.styles.FormStyle" />
+          </java>
+        </serializedBean>
+        <formComponents>
+          <formComponent name="Header" id="36" customizedId="" beanClass="com.install4j.runtime.beans.styles.NestedStyleComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.NestedStyleComponent">
+                  <void property="styleId">
+                    <string>48</string>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <initScript />
+            <visibilityScript />
+            <externalParametrizationPropertyNames />
+          </formComponent>
+          <group name="Main" id="37" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                  <void property="imageEdgeAxisType">
+                    <object class="java.lang.Enum" method="valueOf">
+                      <class>com.install4j.runtime.beans.formcomponents.AxisType</class>
+                      <string>HORIZONTAL</string>
+                    </object>
+                  </void>
+                  <void property="imageFile">
+                    <object class="com.install4j.api.beans.ExternalFile">
+                      <string>../../resources/images/jalview_logo_background_fade-640x480.png</string>
+                    </object>
+                  </void>
+                  <void property="imageOverlap">
+                    <boolean>true</boolean>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <beans>
+              <formComponent name="" id="38" customizedId="" beanClass="com.install4j.runtime.beans.styles.ContentComponent" enabled="true" commentSet="false" comment="" insetTop="10" insetLeft="20" insetBottom="10" insetRight="20" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.ContentComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="Watermark" id="39" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="5" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="true" externalParametrizationName="Jalview" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.SeparatorComponent">
+                      <void property="enabledTitleText">
+                        <boolean>false</boolean>
+                      </void>
+                      <void property="labelText">
+                        <string>install4j</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames>
+                  <propertyName>labelText</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+              <formComponent name="Footer" id="40" customizedId="" beanClass="com.install4j.runtime.beans.styles.NestedStyleComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.NestedStyleComponent">
+                      <void property="styleId">
+                        <string>52</string>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </beans>
+            <externalParametrizationPropertyNames />
+          </group>
+        </formComponents>
+      </style>
+      <style name="Banner" id="41" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.styles.FormStyle" />
+          </java>
+        </serializedBean>
+        <formComponents>
+          <group name="" id="42" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize banner image" externalParametrizationMode="include">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                  <void property="backgroundColor">
+                    <object class="java.awt.Color">
+                      <int>255</int>
+                      <int>255</int>
+                      <int>255</int>
+                      <int>255</int>
+                    </object>
+                  </void>
+                  <void property="borderSides">
+                    <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                      <void property="bottom">
+                        <boolean>true</boolean>
+                      </void>
+                    </object>
+                  </void>
+                  <void property="imageEdgeBackgroundColor">
+                    <object class="java.awt.Color">
+                      <int>25</int>
+                      <int>143</int>
+                      <int>220</int>
+                      <int>255</int>
+                    </object>
+                  </void>
+                  <void property="imageEdgeBorder">
+                    <boolean>true</boolean>
+                  </void>
+                  <void property="imageFile">
+                    <object class="com.install4j.api.beans.ExternalFile">
+                      <string>${compiler:sys.install4jHome}/resource/styles/wizard.png</string>
+                    </object>
+                  </void>
+                  <void property="insets">
+                    <object class="java.awt.Insets">
+                      <int>5</int>
+                      <int>10</int>
+                      <int>10</int>
+                      <int>10</int>
+                    </object>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <beans>
+              <formComponent name="" id="43" customizedId="" beanClass="com.install4j.runtime.beans.styles.ScreenTitleComponent" enabled="true" commentSet="false" comment="" insetTop="0" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.ScreenTitleComponent">
+                      <void property="labelFontSizePercent">
+                        <int>130</int>
+                      </void>
+                      <void property="labelFontStyle">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.formcomponents.FontStyle</class>
+                          <string>BOLD</string>
+                        </object>
+                      </void>
+                      <void property="labelFontType">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.runtime.beans.formcomponents.FontType</class>
+                          <string>DERIVED</string>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="44" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SeparatorComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.formcomponents.SeparatorComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+              <formComponent name="" id="45" customizedId="" beanClass="com.install4j.runtime.beans.styles.ContentComponent" enabled="true" commentSet="false" comment="" insetTop="10" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.styles.ContentComponent" />
+                  </java>
+                </serializedBean>
+                <initScript />
+                <visibilityScript />
+                <externalParametrizationPropertyNames />
+              </formComponent>
+            </beans>
+            <externalParametrizationPropertyNames>
+              <propertyName>imageAnchor</propertyName>
+              <propertyName>imageEdgeBackgroundColor</propertyName>
+              <propertyName>imageFile</propertyName>
+            </externalParametrizationPropertyNames>
+          </group>
+          <formComponent name="" id="46" customizedId="" beanClass="com.install4j.runtime.beans.styles.NestedStyleComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="0" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.NestedStyleComponent">
+                  <void property="styleId">
+                    <string>52</string>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <initScript />
+            <visibilityScript />
+            <externalParametrizationPropertyNames />
+          </formComponent>
+        </formComponents>
+      </style>
+      <group name="Style components" id="47" customizedId="" beanClass="com.install4j.runtime.beans.groups.StyleGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit">
+        <serializedBean>
+          <java class="java.beans.XMLDecoder">
+            <object class="com.install4j.runtime.beans.groups.StyleGroup" />
+          </java>
+        </serializedBean>
+        <beans>
+          <style name="Standard header" id="48" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.FormStyle">
+                  <void property="fillVertical">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="standalone">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="verticalAnchor">
+                    <object class="java.lang.Enum" method="valueOf">
+                      <class>com.install4j.api.beans.Anchor</class>
+                      <string>NORTH</string>
+                    </object>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <formComponents>
+              <group name="" id="49" customizedId="" beanClass="com.install4j.runtime.beans.groups.VerticalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="true" externalParametrizationName="Customize title bar" externalParametrizationMode="include">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.groups.VerticalFormComponentGroup">
+                      <void property="backgroundColor">
+                        <object class="java.awt.Color">
+                          <int>255</int>
+                          <int>255</int>
+                          <int>255</int>
+                          <int>255</int>
+                        </object>
+                      </void>
+                      <void property="borderSides">
+                        <object class="com.install4j.runtime.beans.formcomponents.BorderSides">
+                          <void property="bottom">
+                            <boolean>true</boolean>
+                          </void>
+                        </object>
+                      </void>
+                      <void property="imageAnchor">
+                        <object class="java.lang.Enum" method="valueOf">
+                          <class>com.install4j.api.beans.Anchor</class>
+                          <string>NORTHEAST</string>
+                        </object>
+                      </void>
+                      <void property="imageEdgeBorderWidth">
+                        <int>2</int>
+                      </void>
+                      <void property="imageFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>icon:${installer:sys.installerApplicationMode}_header.png</string>
+                        </object>
+                      </void>
+                      <void property="imageInsets">
+                        <object class="java.awt.Insets">
+                          <int>0</int>
+                          <int>5</int>
+                          <int>1</int>
+                          <int>1</int>
+                        </object>
+                      </void>
+                      <void property="insets">
+                        <object class="java.awt.Insets">
+                          <int>0</int>
+                          <int>20</int>
+                          <int>0</int>
+                          <int>10</int>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <beans>
+                  <formComponent name="Title" id="50" customizedId="" beanClass="com.install4j.runtime.beans.styles.ScreenTitleComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.ScreenTitleComponent">
+                          <void property="labelFontStyle">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.runtime.beans.formcomponents.FontStyle</class>
+                              <string>BOLD</string>
+                            </object>
+                          </void>
+                          <void property="labelFontType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.runtime.beans.formcomponents.FontType</class>
+                              <string>DERIVED</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Subtitle" id="51" customizedId="" beanClass="com.install4j.runtime.beans.styles.ScreenTitleComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="8" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.ScreenTitleComponent">
+                          <void property="titleType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.runtime.beans.styles.TitleType</class>
+                              <string>SUB_TITLE</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                </beans>
+                <externalParametrizationPropertyNames>
+                  <propertyName>backgroundColor</propertyName>
+                  <propertyName>foregroundColor</propertyName>
+                  <propertyName>imageAnchor</propertyName>
+                  <propertyName>imageFile</propertyName>
+                  <propertyName>imageOverlap</propertyName>
+                </externalParametrizationPropertyNames>
+              </group>
+            </formComponents>
+          </style>
+          <style name="Standard footer" id="52" customizedId="" beanClass="com.install4j.runtime.beans.styles.FormStyle" enabled="true" commentSet="false" comment="">
+            <serializedBean>
+              <java class="java.beans.XMLDecoder">
+                <object class="com.install4j.runtime.beans.styles.FormStyle">
+                  <void property="fillVertical">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="standalone">
+                    <boolean>false</boolean>
+                  </void>
+                  <void property="verticalAnchor">
+                    <object class="java.lang.Enum" method="valueOf">
+                      <class>com.install4j.api.beans.Anchor</class>
+                      <string>SOUTH</string>
+                    </object>
+                  </void>
+                </object>
+              </java>
+            </serializedBean>
+            <formComponents>
+              <group name="" id="53" customizedId="" beanClass="com.install4j.runtime.beans.groups.HorizontalFormComponentGroup" enabled="true" commentSet="false" comment="" actionElevationType="inherit" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                <serializedBean>
+                  <java class="java.beans.XMLDecoder">
+                    <object class="com.install4j.runtime.beans.groups.HorizontalFormComponentGroup">
+                      <void property="alignFirstLabel">
+                        <boolean>false</boolean>
+                      </void>
+                      <void property="insets">
+                        <object class="java.awt.Insets">
+                          <int>3</int>
+                          <int>5</int>
+                          <int>8</int>
+                          <int>5</int>
+                        </object>
+                      </void>
+                    </object>
+                  </java>
+                </serializedBean>
+                <beans>
+                  <formComponent name="" id="54" customizedId="" beanClass="com.install4j.runtime.beans.formcomponents.SpringComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.formcomponents.SpringComponent" />
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Back button" id="55" customizedId="" beanClass="com.install4j.runtime.beans.styles.StandardControlButtonComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.StandardControlButtonComponent">
+                          <void property="buttonText">
+                            <string>&lt; ${i18n:ButtonBack}</string>
+                          </void>
+                          <void property="controlButtonType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.api.context.ControlButtonType</class>
+                              <string>PREVIOUS</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Next button" id="56" customizedId="" beanClass="com.install4j.runtime.beans.styles.StandardControlButtonComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.StandardControlButtonComponent">
+                          <void property="buttonText">
+                            <string>${i18n:ButtonNext} &gt;</string>
+                          </void>
+                          <void property="controlButtonType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.api.context.ControlButtonType</class>
+                              <string>NEXT</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                  <formComponent name="Cancel button" id="57" customizedId="" beanClass="com.install4j.runtime.beans.styles.StandardControlButtonComponent" enabled="true" commentSet="false" comment="" insetTop="" insetLeft="5" insetBottom="" insetRight="" resetInitOnPrevious="false" useExternalParametrization="false" externalParametrizationName="" externalParametrizationMode="all">
+                    <serializedBean>
+                      <java class="java.beans.XMLDecoder">
+                        <object class="com.install4j.runtime.beans.styles.StandardControlButtonComponent">
+                          <void property="buttonText">
+                            <string>${i18n:ButtonCancel}</string>
+                          </void>
+                          <void property="controlButtonType">
+                            <object class="java.lang.Enum" method="valueOf">
+                              <class>com.install4j.api.context.ControlButtonType</class>
+                              <string>CANCEL</string>
+                            </object>
+                          </void>
+                        </object>
+                      </java>
+                    </serializedBean>
+                    <initScript />
+                    <visibilityScript />
+                    <externalParametrizationPropertyNames />
+                  </formComponent>
+                </beans>
+                <externalParametrizationPropertyNames />
+              </group>
+            </formComponents>
+          </style>
+        </beans>
+      </group>
+    </styles>
+  </installerGui>
+  <mediaSets>
+    <windows name="Windows" id="130" customizedId="" mediaFileName="" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="64" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="windows-amd64-11.0.2" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="" contentFilesType="1" verifyIntegrity="true">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="121" />
+        <launcher id="737" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+        <entry location=".i4j_fileset_1015" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+    </windows>
+    <macos name="macOS Single Bundle Archive with Getdown JRE" id="131" customizedId="" mediaFileName="${compiler:sys.shortName}_${compiler:sys.platform}-installer_${compiler:sys.version}" installDir="${compiler:sys.fullName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="macosx-amd64-1.8.0_202_unpacked" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" requiredVmIdPrefix="" customInstallBaseDir="" contentFilesType="1" installerName="${i18n:InstallerName(${compiler:sys.fullName})}" volumeName="${compiler:sys.shortName}" compressDmg="true" launcherId="121">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_734" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <topLevelFiles />
+    </macos>
+    <unixInstaller name="Unix Installer" id="132" customizedId="" mediaFileName="" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="" contentFilesType="1">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="737" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <installerScript mode="1" file="">
+        <content />
+      </installerScript>
+    </unixInstaller>
+    <macosArchive name="macOS Single Bundle Archive" id="152" customizedId="" mediaFileName="${compiler:sys.shortName}_${compiler:sys.platform}-app_${compiler:sys.version}" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="macosx-amd64-11.0.2" manualJREEntry="false" archiveType="dmg" volumeName="${compiler:sys.shortName}" launcherId="121">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <topLevelFiles>
+        <symlink name="&quot; &quot;" target="/Applications" />
+        <file name=".background/jalview_dmg_background.png" file="../../resources/install4j/jalview_dmg_background.png" />
+        <file name=".DS_Store" file="../../resources/install4j/DS_Store" />
+      </topLevelFiles>
+    </macosArchive>
+    <linuxDeb name="Linux Deb Archive" id="153" customizedId="" mediaFileName="" installDir="/opt/${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" overwriteNeverAsConfigFiles="false" dependencies="" bzip="true" description="Jalview Desktop" maintainerEmail="help@jalview.org" architectureSet="false" architecture="">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="121" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <preInstallScript mode="1" file="">
+        <content />
+      </preInstallScript>
+      <postInstallScript mode="1" file="">
+        <content />
+      </postInstallScript>
+      <preUninstallScript mode="1" file="">
+        <content />
+      </preUninstallScript>
+      <postUninstallScript mode="1" file="">
+        <content />
+      </postUninstallScript>
+    </linuxDeb>
+    <linuxRPM name="Linux RPM" id="570" customizedId="" mediaFileName="" installDir="/opt/${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" overwriteNeverAsConfigFiles="false" dependencies="" os="linux" arch="i386">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="121" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <preInstallScript mode="1" file="">
+        <content />
+      </preInstallScript>
+      <postInstallScript mode="1" file="">
+        <content />
+      </postInstallScript>
+      <preUninstallScript mode="1" file="">
+        <content />
+      </preUninstallScript>
+      <postUninstallScript mode="1" file="">
+        <content />
+      </postUninstallScript>
+    </linuxRPM>
+    <macosArchive name="Offline macOS Single Bundle Archive" id="739" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}-app_${compiler:sys.version}" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="macosx-amd64-11.0.2" manualJREEntry="false" archiveType="dmg" volumeName="${compiler:sys.shortName} Offline Installer" launcherId="737">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <topLevelFiles>
+        <symlink name="&quot; &quot;" target="/Applications" />
+        <file name=".background/jalview_dmg_background.png" file="../../resources/install4j/jalview_dmg_background.png" />
+        <file name=".DS_Store" file="../../resources/install4j/DS_Store" />
+      </topLevelFiles>
+    </macosArchive>
+    <unixInstaller name="Offline Unix Installer" id="741" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}_${compiler:sys.version}" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="" contentFilesType="1">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="121" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <installerScript mode="1" file="">
+        <content />
+      </installerScript>
+    </unixInstaller>
+    <windows name="Offline Windows" id="743" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}_${compiler:sys.version}" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="64" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="false" includedJRE="" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="" contentFilesType="1" verifyIntegrity="true">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedLaunchers>
+        <launcher id="121" />
+        <launcher id="737" />
+      </excludedLaunchers>
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_734" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+        <entry location=".i4j_fileset_1015" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+    </windows>
+    <macosArchive name="Offline macOS Single Bundle Archive with Getdown JRE" id="878" customizedId="" mediaFileName="${compiler:sys.shortName}-OFFLINE_${compiler:sys.platform}-app_${compiler:sys.version}" installDir="${compiler:sys.shortName}" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" downloadURL="" includeAllDownloadableComponents="true" includedJRE="" manualJREEntry="false" archiveType="dmg" volumeName="${compiler:sys.shortName} Offline Installer" launcherId="737">
+      <excludedComponents />
+      <includedDownloadableComponents />
+      <excludedBeans />
+      <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+      <exclude>
+        <entry location=".i4j_fileset_" fileType="regular" />
+        <entry location=".i4j_fileset_880" fileType="regular" />
+        <entry location=".i4j_fileset_882" fileType="regular" />
+      </exclude>
+      <variables />
+      <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+        <commentFiles />
+        <customAttributes />
+      </autoUpdate>
+      <topLevelFiles>
+        <symlink name="&quot; &quot;" target="/Applications" />
+        <file name=".background/jalview_dmg_background.png" file="./jalview_dmg_background.png" />
+        <file name=".DS_Store" file="./DS_Store" />
+      </topLevelFiles>
+    </macosArchive>
+  </mediaSets>
+  <buildIds buildAll="false">
+    <mediaSet refId="153" />
+    <mediaSet refId="570" />
+    <mediaSet refId="743" />
+    <mediaSet refId="878" />
+  </buildIds>
+  <buildOptions verbose="true" faster="true" disableSigning="true" disableJreBundling="false" debug="false" />
+</install4j>
diff --git a/utils/install4j/uninstall_app_dmg_file_inclusions.sh b/utils/install4j/uninstall_app_dmg_file_inclusions.sh
new file mode 100644 (file)
index 0000000..f5bed50
--- /dev/null
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+find Uninstall\ Old\ Jalview.app | perl -p -e 'chomp;$_=qq(            <file name=").$_.qq(" file="./).$_.qq(" />\n);' > uninstall_old_jalview_files.xml
+
+# makes the file used to replace the line
+# <file name="UNINSTALL_OLD_JALVIEW_APP_REPLACED_IN_GRADLE" file="./Uninstall Old Jalview.app" />
+# (replacement happens in gradle)
diff --git a/utils/install4j/uninstall_old_jalview.icns b/utils/install4j/uninstall_old_jalview.icns
new file mode 100644 (file)
index 0000000..67a2cbd
Binary files /dev/null and b/utils/install4j/uninstall_old_jalview.icns differ
diff --git a/utils/install4j/uninstall_old_jalview_files.xml b/utils/install4j/uninstall_old_jalview_files.xml
new file mode 100644 (file)
index 0000000..342fd90
--- /dev/null
@@ -0,0 +1,13 @@
+            <file name="Uninstall Old Jalview.app" file="./Uninstall Old Jalview.app" />
+            <file name="Uninstall Old Jalview.app/Contents" file="./Uninstall Old Jalview.app/Contents" />
+            <file name="Uninstall Old Jalview.app/Contents/MacOS" file="./Uninstall Old Jalview.app/Contents/MacOS" />
+            <file name="Uninstall Old Jalview.app/Contents/MacOS/applet" file="./Uninstall Old Jalview.app/Contents/MacOS/applet" />
+            <file name="Uninstall Old Jalview.app/Contents/Resources" file="./Uninstall Old Jalview.app/Contents/Resources" />
+            <file name="Uninstall Old Jalview.app/Contents/Resources/applet.rsrc" file="./Uninstall Old Jalview.app/Contents/Resources/applet.rsrc" />
+            <file name="Uninstall Old Jalview.app/Contents/Resources/description.rtfd" file="./Uninstall Old Jalview.app/Contents/Resources/description.rtfd" />
+            <file name="Uninstall Old Jalview.app/Contents/Resources/description.rtfd/TXT.rtf" file="./Uninstall Old Jalview.app/Contents/Resources/description.rtfd/TXT.rtf" />
+            <file name="Uninstall Old Jalview.app/Contents/Resources/Scripts" file="./Uninstall Old Jalview.app/Contents/Resources/Scripts" />
+            <file name="Uninstall Old Jalview.app/Contents/Resources/Scripts/main.scpt" file="./Uninstall Old Jalview.app/Contents/Resources/Scripts/main.scpt" />
+            <file name="Uninstall Old Jalview.app/Contents/Resources/applet.icns" file="./Uninstall Old Jalview.app/Contents/Resources/applet.icns" />
+            <file name="Uninstall Old Jalview.app/Contents/Info.plist" file="./Uninstall Old Jalview.app/Contents/Info.plist" />
+            <file name="Uninstall Old Jalview.app/Contents/PkgInfo" file="./Uninstall Old Jalview.app/Contents/PkgInfo" />
diff --git a/utils/jdeps_jlink_all.sh b/utils/jdeps_jlink_all.sh
new file mode 100755 (executable)
index 0000000..9f79d2d
--- /dev/null
@@ -0,0 +1,23 @@
+#/usr/bin/env bash
+
+# Be in the jalview top level dir.
+# lib -- contains usual jalview jar files
+#        make sure it contains the extra jar files needed in classpath for Java 11
+# j11lib -- contains java11 style modules to be put into the JRE (not needed at runtime)
+#
+# j11jre -- dir containing JRE environments for jalview
+#
+# creates file modules.new which is comma-separated list of modules needed, can be used like this in jlink argument
+# and a java 11 JRE in j11jre/jre-new
+
+( for x in lib/*.jar j11lib/*.jar dist/jalview.jar; do echo $x >&2; jdeps --list-deps --module-path j11lib $x | grep -v Warning: | grep -v "JDK removed" | sed -e 's/^ *//;s/\/.*//;s/$/,/;'; done ) | sort -u | perl -p -e 'chomp;' | perl -p -e 's/,$//;chomp;' > modules.new
+
+if [ x$JAVA_HOME != x ]; then
+  jlink --no-header-files --no-man-pages --strip-debug --module-path "$JAVA_HOME/jmods:j11lib" --add-modules `cat modules.new` --compress=2 --output j11jre/jre-new
+else
+  jlink --no-header-files --no-man-pages --strip-debug --module-path "j11lib" --add-modules `cat modules.new` --compress=2 --output j11jre/jre-new
+fi
+
+
+# or if you're in a hurry for a one-liner...
+#jlink --no-header-files --no-man-pages --strip-debug --module-path "$JAVA_HOME/jmods:j11lib" --add-modules ` ( for x in lib/*.jar j11lib/*.jar dist/jalview.jar; do echo $x >&2; jdeps --list-deps --module-path j11mod $x | grep -v "Warning:" | grep -v "JDK removed" | sed -e 's/^ *//;s/\/.*//;s/$/,/;'; done ) | sort -u | perl -p -e 'chomp;' | perl -p -e 's/,$//;chomp;' ` --compress=2 --output j11jre/jre-new
diff --git a/utils/modulify-jar.sh b/utils/modulify-jar.sh
new file mode 100755 (executable)
index 0000000..f508ed2
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+
+CMD=$(basename $0)
+
+usage() {
+  echo "Usage: $CMD /path/to/jarfile" >&2
+}
+
+usagexit() {
+  usage
+  exit 1
+}
+
+error() {
+  echo $1 >&2
+  usagexit
+}
+
+JARFILE=$1
+[ -z $JARFILE ] && usagexit
+[ -f $JARFILE ] || error "No file $JARFILE"
+[ -r $JARFILE ] || error "$JARFILE not readable"
+
+EXT=.jar
+SUFFIX=-MODULE
+FILENAME=$(basename $JARFILE)
+BASE=$(basename -s $EXT $JARFILE)
+DIR=$(dirname $JARFILE)
+
+# set absolute path to $JARFILE if not specified
+[ x${DIR#/} = x$DIR ] && DIR=$(cd "$DIR" && pwd)
+
+ABSJARFILE=$DIR/$FILENAME
+
+TMPDIR=/tmp/$USER-$CMD-$BASE-$$
+
+[ x$FILENAME = x$BASE ] && error "Should be $EXT file"
+
+mkdir -p $TMPDIR/jar || error "Could not create tmp dir $TMPDIR/jar"
+mkdir -p $TMPDIR/info || error "Could not create tmp dir $TMPDIR/info"
+cd $TMPDIR/jar
+jar -xvf $ABSJARFILE > /dev/null
+jdeps --module-path="$DIR" --generate-module-info $TMPDIR/info $ABSJARFILE
+# next line assuming only one module-info.java file created, I think this is always true...? It'll just use the last one if not.
+find $TMPDIR/info -name "module-info.java" -exec /bin/mv {} . \;
+[ -e ./module-info.java ] || error "No module-info.java file found in $TMPDIR/info"
+javac -d $TMPDIR/jar ./module-info.java
+jar -cvf $DIR/${BASE}${SUFFIX}${EXT} -C $TMPDIR/jar . > /dev/null
+rm -rf $TMPDIR
+
+
diff --git a/utils/publishHelp.xml b/utils/publishHelp.xml
new file mode 100644 (file)
index 0000000..c626a21
--- /dev/null
@@ -0,0 +1,64 @@
+<project name="Publish Help Webpages for Jalview" default="pubhtmlhelp">
+
+<target name="pubhtmlhelp">
+  <property name="appletHelpDir" value="../build/distributions/help"/>
+        <!-- finally, publish the help files -->
+        <javac srcdir="." destdir="." includes="help2Website.java"/>
+        <java fork="true" dir="${helpBuildDir}" classpath="." classname="help2Website"/>
+        <delete dir="${appletHelpDir}"/>
+        <copy preservelastmodified="true" overwrite="true" 
+           todir="${appletHelpDir}">
+          <fileset dir="${helpBuildDir}">
+            <include name="*htm*"/>
+            <include name="icons/**.*"/>
+            <include name="html/**/*.*"/>
+            <exclude name="**/CVS"/>
+            <exclude name="CVS"/>
+          </fileset>
+        </copy>
+        <replace dir="${appletHelpDir}">
+        <include name="**/*.htm*"/>
+        <replacetoken><![CDATA[</body>]]></replacetoken>
+        <replacevalue><![CDATA[
+<script type="text/javascript">
+    var gaJsHost = (("https:" == document.location.protocol) ?
+        "https://ssl." : "http://www.");
+    document.write(unescape("%3Cscript src=\'" + gaJsHost +
+        "google-analytics.com/ga.js\' type=\'text/javascript\'%3E%3C/script%3E"));
+</script>
+<script type="text/javascript">
+try{
+    var pageTracker = _gat._getTracker("'UA-9060947-1'");
+    pageTracker._trackPageview();
+} catch(err) {}
+</script>
+</body>
+]]></replacevalue>
+</replace>
+<echo file="${appletHelpDir}/help.html">
+<![CDATA[<html><head>
+<title>Jalview Documentation</title>
+</head>
+<frameset cols="300,*">
+<frame src="helpTOC.html" name=tocframe scrolling=yes >
+<frame src="html/index.html" name=bodyframe >
+<noframes><body>
+This page requires a browser that supports frames.
+<script type="text/javascript">
+    var gaJsHost = (("https:" == document.location.protocol) ?
+        "https://ssl." : "http://www.");
+    document.write(unescape("%3Cscript src=\'" + gaJsHost +
+        "google-analytics.com/ga.js\' type=\'text/javascript\'%3E%3C/script%3E"));
+</script>
+<script type="text/javascript">
+try{
+    var pageTracker = _gat._getTracker("'UA-9060947-1'");
+    pageTracker._trackPageview();
+} catch(err) {}
+</script></body>
+<noframes></frameset>
+</html>
+]]></echo>
+
+  </target>
+</project>
diff --git a/utils/showJVMVersion.java b/utils/showJVMVersion.java
new file mode 100644 (file)
index 0000000..671abc4
--- /dev/null
@@ -0,0 +1,5 @@
+public class showJVMVersion {
+ public static void main(String args[]) {
+   System.out.println(System.getProperty("java.version"));
+ }
+}