Merge branch 'develop' into JAL-3416_different_look_and_feel_on_linux
authorBen Soares <b.soares@dundee.ac.uk>
Mon, 4 Oct 2021 16:46:03 +0000 (17:46 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Mon, 4 Oct 2021 16:46:03 +0000 (17:46 +0100)
424 files changed:
README [changed mode: 0755->0644]
THIRDPARTYLIBS
build.gradle
doc/README-DMG_background_image_creation.md [moved from utils/install4j/README-DMG_background_image_creation.md with 100% similarity]
doc/README-DMG_creation.md [moved from utils/install4j/README-DMG_creation.md with 100% similarity]
doc/README_convert_PNG_to_ICNS_and_ICO [moved from utils/install4j/README_convert_PNG_to_ICNS_and_ICO with 62% similarity]
doc/old_README [new file with mode: 0755]
gradle.properties
help/help/help.jhm
help/help/helpTOC.xml
help/help/html/features/annotationsFormat.html
help/help/html/features/clarguments.html
help/help/html/features/commandline.html
help/help/html/features/groovy.html
help/help/html/features/jvlfiles.html
help/help/html/features/preferences.html
help/help/html/features/search.html
help/help/html/features/search.png
help/help/html/features/seqfetch.html
help/help/html/groovy/featuresCounter.html
help/help/html/index.html
help/help/html/keys.html
help/help/html/logging.html [new file with mode: 0644]
help/help/html/memory.html
help/help/html/releases.html
help/help/html/webServices/newsreader.html
help/help/html/whatsNew.html
j11lib/Jmol-14.31.53.jar [new file with mode: 0644]
j11lib/Jmol-15.1.3.jar [deleted file]
j11lib/groovy-all-2.4.21-indy.jar [moved from j11lib/groovy-all-2.4.12-indy.jar with 58% similarity]
j8lib/Jmol-14.29.17.jar [deleted file]
j8lib/Jmol-14.31.53.jar [new file with mode: 0644]
j8lib/groovy-all-2.4.21-indy.jar [moved from j8lib/groovy-all-2.4.12-indy.jar with 58% similarity]
lib/Jmol-14.29.17.jar [deleted file]
resources/fts/pdb_data_columns.txt
resources/fts/tdbeacons_data_columns.txt [new file with mode: 0644]
resources/images/3d-beacons-logo-transparent.png [new file with mode: 0644]
resources/lang/Messages.properties
resources/lang/Messages_es.properties
schemas/vamsas.xsd
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java
src/ext/edu/ucsf/rbvi/strucviz2/StructureManager.java
src/jalview/analysis/AlignSeq.java
src/jalview/analysis/AlignmentUtils.java
src/jalview/analysis/AnnotationSorter.java
src/jalview/analysis/Conservation.java
src/jalview/analysis/Finder.java
src/jalview/analysis/GeneticCodes.java
src/jalview/analysis/SequenceIdMatcher.java
src/jalview/api/DBRefEntryI.java
src/jalview/api/FeatureRenderer.java
src/jalview/api/FeatureSettingsModelI.java
src/jalview/api/FeaturesDisplayedI.java
src/jalview/bin/Cache.java
src/jalview/bin/HiDPISetting.java
src/jalview/bin/Jalview.java
src/jalview/bin/JalviewLite.java
src/jalview/bin/Launcher.java
src/jalview/bin/MemorySetting.java
src/jalview/commands/ChangeCaseCommand.java
src/jalview/commands/EditCommand.java
src/jalview/datamodel/AlignmentAnnotation.java
src/jalview/datamodel/DBRefEntry.java
src/jalview/datamodel/DBRefSource.java
src/jalview/datamodel/MappedFeatures.java
src/jalview/datamodel/Mapping.java
src/jalview/datamodel/PDBEntry.java
src/jalview/datamodel/ResidueCount.java
src/jalview/datamodel/Sequence.java
src/jalview/datamodel/SequenceI.java
src/jalview/datamodel/features/FeatureMatcher.java
src/jalview/datamodel/features/FeatureMatcherSet.java
src/jalview/ext/ensembl/EnsemblGene.java
src/jalview/ext/ensembl/EnsemblInfo.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/jmol/JmolParser.java
src/jalview/ext/rbvi/chimera/ChimeraCommands.java
src/jalview/ext/rbvi/chimera/ChimeraXCommands.java
src/jalview/fts/api/FTSRestClientI.java
src/jalview/fts/api/StructureFTSRestClientI.java [new file with mode: 0644]
src/jalview/fts/core/FTSDataColumnPreferences.java
src/jalview/fts/core/FTSRestClient.java
src/jalview/fts/core/FTSRestRequest.java
src/jalview/fts/core/FTSRestResponse.java
src/jalview/fts/service/alphafold/AlphafoldRestClient.java [new file with mode: 0644]
src/jalview/fts/service/pdb/PDBFTSRestClient.java
src/jalview/fts/service/threedbeacons/.gitignore [new file with mode: 0644]
src/jalview/fts/service/threedbeacons/TDB_FTSData.java [new file with mode: 0644]
src/jalview/fts/service/threedbeacons/TDBeaconsFTSPanel.java [new file with mode: 0644]
src/jalview/fts/service/threedbeacons/TDBeaconsFTSRestClient.java [new file with mode: 0644]
src/jalview/gui/AlignFrame.java
src/jalview/gui/AlignViewport.java
src/jalview/gui/AnnotationExporter.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/AppJmol.java
src/jalview/gui/AppJmolBinding.java
src/jalview/gui/BlogReader.java
src/jalview/gui/ChimeraViewFrame.java
src/jalview/gui/CutAndPasteTransfer.java
src/jalview/gui/Desktop.java
src/jalview/gui/FeatureSettings.java
src/jalview/gui/Finder.java
src/jalview/gui/JDatabaseTree.java
src/jalview/gui/JalviewChimeraXBindingModel.java
src/jalview/gui/OptsAndParamsPage.java
src/jalview/gui/PopupMenu.java
src/jalview/gui/Preferences.java
src/jalview/gui/SeqCanvas.java
src/jalview/gui/SeqPanel.java
src/jalview/gui/SequenceFetcher.java
src/jalview/gui/Slider.java
src/jalview/gui/SplitFrame.java
src/jalview/gui/StructureChooser.java
src/jalview/gui/StructureViewer.java
src/jalview/gui/StructureViewerBase.java
src/jalview/gui/TreePanel.java
src/jalview/gui/UserDefinedColours.java
src/jalview/gui/WebserviceInfo.java
src/jalview/gui/WsJobParameters.java
src/jalview/gui/structurechooser/PDBStructureChooserQuerySource.java [new file with mode: 0644]
src/jalview/gui/structurechooser/StructureChooserQuerySource.java [new file with mode: 0644]
src/jalview/gui/structurechooser/TDBResultAnalyser.java [new file with mode: 0644]
src/jalview/gui/structurechooser/ThreeDBStructureChooserQuerySource.java [new file with mode: 0644]
src/jalview/io/AnnotationFile.java
src/jalview/io/AppletFormatAdapter.java
src/jalview/io/EMBLLikeFlatFile.java [new file with mode: 0644]
src/jalview/io/EmblFlatFile.java [new file with mode: 0644]
src/jalview/io/FeaturesFile.java
src/jalview/io/FileFormat.java
src/jalview/io/FileFormats.java
src/jalview/io/FileLoader.java
src/jalview/io/FormatAdapter.java
src/jalview/io/GenBankFile.java [new file with mode: 0644]
src/jalview/io/IdentifyFile.java
src/jalview/io/JPredFile.java
src/jalview/io/JSONFile.java
src/jalview/io/JalviewFileFilter.java
src/jalview/io/JalviewFileView.java
src/jalview/io/JnetAnnotationMaker.java
src/jalview/io/MSFfile.java
src/jalview/io/NewickFile.java
src/jalview/io/PDBFeatureSettings.java
src/jalview/io/SequenceAnnotationReport.java
src/jalview/io/StockholmFile.java
src/jalview/io/StructureFile.java
src/jalview/io/gff/ExonerateHelper.java
src/jalview/io/packed/ParsePackedSet.java
src/jalview/io/vcf/VCFLoader.java
src/jalview/javascript/log4j/Level.java
src/jalview/jbgui/FilterOption.java [new file with mode: 0644]
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GStructureChooser.java
src/jalview/json/binding/biojson/v1/ColourSchemeMapper.java
src/jalview/project/Jalview2XML.java
src/jalview/renderer/AnnotationRenderer.java
src/jalview/schemes/ClustalxColourScheme.java
src/jalview/schemes/ColourSchemeLoader.java
src/jalview/schemes/ColourSchemes.java
src/jalview/schemes/FeatureColour.java
src/jalview/schemes/FeatureSettingsAdapter.java
src/jalview/schemes/ResidueProperties.java
src/jalview/schemes/UserColourScheme.java
src/jalview/structure/StructureImportSettings.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/structures/models/AAStructureBindingModel.java
src/jalview/util/CaseInsensitiveString.java
src/jalview/util/ChannelProperties.java
src/jalview/util/ColorUtils.java
src/jalview/util/Comparison.java
src/jalview/util/DBRefUtils.java
src/jalview/util/DnaUtils.java
src/jalview/util/HttpUtils.java
src/jalview/util/MapList.java
src/jalview/util/MappingUtils.java
src/jalview/util/MessageManager.java
src/jalview/util/ParseHtmlBodyAndLinks.java
src/jalview/util/Platform.java
src/jalview/util/StringUtils.java
src/jalview/util/matcher/Matcher.java
src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java
src/jalview/viewmodel/seqfeatures/FeaturesDisplayed.java
src/jalview/ws/DBRefFetcher.java
src/jalview/ws/SequenceFetcher.java
src/jalview/ws/dbsources/EBIAlfaFold.java [new file with mode: 0644]
src/jalview/ws/dbsources/EmblCdsSource.java
src/jalview/ws/dbsources/EmblFlatfileSource.java [new file with mode: 0644]
src/jalview/ws/dbsources/EmblSource.java
src/jalview/ws/dbsources/EmblXmlSource.java
src/jalview/ws/dbsources/TDBeacons.java [new file with mode: 0644]
src/jalview/ws/dbsources/Uniprot.java
src/jalview/ws/dbsources/Xfam.java
src/jalview/ws/ebi/EBIFetchClient.java
src/jalview/ws/io/mime/MimeTypes.java
src/jalview/ws/jws1/JPredClient.java
src/jalview/ws/jws1/MsaWSClient.java
src/jalview/ws/jws2/Jws2Client.java
src/jalview/ws/jws2/MsaWSClient.java
src/jalview/ws/jws2/SequenceAnnotationWSClient.java
src/jalview/ws/jws2/dm/JabaOption.java
src/jalview/ws/rest/InputType.java
src/jalview/ws/rest/RestJobThread.java
src/jalview/ws/sifts/SiftsClient.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/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/mc_view/PDBChain.java
src/mc_view/PDBfile.java
swingjs/README.txt [deleted file]
swingjs/SwingJS-site.zip
swingjs/differences.txt [new file with mode: 0644]
swingjs/net.sf.j2s.core-j11.jar
swingjs/net.sf.j2s.core.jar
swingjs/timestamp
swingjs/ver/3.2.10-j11/DEV_NOTES.txt [moved from swingjs/ver/3.2.4/DEV_NOTES.txt with 100% similarity]
swingjs/ver/3.2.10-j11/SwingJS-site.zip [new file with mode: 0644]
swingjs/ver/3.2.10-j11/_j2sclasslist.txt [moved from swingjs/ver/3.2.4/_j2sclasslist.txt with 95% similarity]
swingjs/ver/3.2.10-j11/differences.txt [new file with mode: 0644]
swingjs/ver/3.2.10-j11/net.sf.j2s.core-j11.jar [new file with mode: 0644]
swingjs/ver/3.2.10-j11/timestamp [new file with mode: 0644]
swingjs/ver/3.2.10/DEV_NOTES.txt [new file with mode: 0644]
swingjs/ver/3.2.10/SwingJS-site.zip [new file with mode: 0644]
swingjs/ver/3.2.10/_j2sclasslist.txt [new file with mode: 0644]
swingjs/ver/3.2.10/differences.txt [new file with mode: 0644]
swingjs/ver/3.2.10/net.sf.j2s.core.jar [new file with mode: 0644]
swingjs/ver/3.2.10/timestamp [new file with mode: 0644]
swingjs/ver/3.2.4/SwingJS-site.zip [deleted file]
swingjs/ver/3.2.4/net.sf.j2s.core.jar [deleted file]
swingjs/ver/3.2.4/timestamp [deleted file]
swingjs/ver/3.2.5/SwingJS-site.zip
swingjs/ver/3.2.5/_j2sclasslist.txt
swingjs/ver/3.2.5/differences.txt [new file with mode: 0644]
swingjs/ver/3.2.5/net.sf.j2s.core.jar
swingjs/ver/3.2.5/timestamp
swingjs/ver/3.2.7/SwingJS-site.zip
swingjs/ver/3.2.8/SwingJS-site.zip
swingjs/ver/3.2.9-j11/SwingJS-site.zip
swingjs/ver/3.2.9-j11/differences.txt [new file with mode: 0644]
swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar [new file with mode: 0644]
swingjs/ver/3.2.9-j11/net.sf.j2s.core.jar [deleted file]
swingjs/ver/3.2.9-j11/timestamp
swingjs/ver/3.2.9/SwingJS-site.zip
swingjs/ver/3.2.9/differences.txt [new file with mode: 0644]
swingjs/ver/3.2.9/net.sf.j2s.core-j11.jar [deleted file]
swingjs/ver/3.2.9/net.sf.j2s.core.jar
swingjs/ver/3.2.9/timestamp
swingjs/ver/3.3.1-j11/DEV_NOTES.txt [new file with mode: 0644]
swingjs/ver/3.3.1-j11/SwingJS-site.zip [new file with mode: 0644]
swingjs/ver/3.3.1-j11/_j2sclasslist.txt [new file with mode: 0644]
swingjs/ver/3.3.1-j11/differences.txt [new file with mode: 0644]
swingjs/ver/3.3.1-j11/net.sf.j2s.core-j11.jar [new file with mode: 0644]
swingjs/ver/3.3.1-j11/timestamp [new file with mode: 0644]
swingjs/ver/3.3.1/DEV_NOTES.txt [new file with mode: 0644]
swingjs/ver/3.3.1/SwingJS-site.zip [new file with mode: 0644]
swingjs/ver/3.3.1/_j2sclasslist.txt [new file with mode: 0644]
swingjs/ver/3.3.1/differences.txt [new file with mode: 0644]
swingjs/ver/3.3.1/net.sf.j2s.core.jar [new file with mode: 0644]
swingjs/ver/3.3.1/timestamp [new file with mode: 0644]
swingjs/ver/pre-long/DEV_NOTES.txt [new file with mode: 0644]
swingjs/ver/pre-long/README-pre-long.txt [new file with mode: 0644]
swingjs/ver/pre-long/SwingJS-site.zip [new file with mode: 0644]
swingjs/ver/pre-long/_j2sclasslist.txt [new file with mode: 0644]
swingjs/ver/pre-long/differences.txt [new file with mode: 0644]
swingjs/ver/pre-long/net.sf.j2s.core.jar [new file with mode: 0644]
swingjs/ver/pre-long/timestamp [new file with mode: 0644]
test/jalview/analysis/AlignmentGenerator.java
test/jalview/analysis/RnaTest.java
test/jalview/commands/EditCommandTest.java
test/jalview/datamodel/DBRefEntryTest.java
test/jalview/datamodel/PDBEntryTest.java
test/jalview/datamodel/ResidueCountTest.java
test/jalview/datamodel/SequenceTest.java
test/jalview/ext/ensembl/EnsemblCdnaTest.java
test/jalview/ext/ensembl/EnsemblGeneTest.java
test/jalview/ext/jmol/JmolParserTest.java
test/jalview/ext/paradise/TestAnnotate3D.java
test/jalview/ext/rbvi/chimera/ChimeraXCommandsTest.java
test/jalview/fts/service/pdb/PDBFTSPanelTest.java
test/jalview/fts/service/pdb/PDBFTSRestClientTest.java
test/jalview/fts/threedbeacons/.gitignore [new file with mode: 0644]
test/jalview/fts/threedbeacons/TDBeaconsFTSRestClientTest.java [new file with mode: 0644]
test/jalview/fts/threedbeacons/TDBeaconsPanelTest.java [new file with mode: 0644]
test/jalview/fts/threedbeacons/p01308_pdbfts_query.txt [new file with mode: 0644]
test/jalview/fts/threedbeacons/p01308_pdbfts_resp.txt [new file with mode: 0644]
test/jalview/fts/threedbeacons/p01308_tdb_resp.txt [new file with mode: 0644]
test/jalview/gui/SeqPanelTest.java
test/jalview/gui/StructureChooserTest.java
test/jalview/gui/structurechooser/StructureChooserQuerySourceTest.java [new file with mode: 0644]
test/jalview/io/AnnotationFileIOTest.java
test/jalview/io/EmblFlatFileTest.java [new file with mode: 0644]
test/jalview/io/FileFormatsTest.java
test/jalview/io/GenBankFileTest.java [new file with mode: 0644]
test/jalview/io/IdentifyFileTest.java
test/jalview/io/J03321.embl.txt [new file with mode: 0644]
test/jalview/io/J03321.gb [new file with mode: 0644]
test/jalview/io/J03321_rna.embl.txt [new file with mode: 0644]
test/jalview/util/MapListTest.java
test/jalview/util/MappingUtilsTest.java
test/jalview/ws/dbsources/EmblXmlSourceTest.java [moved from test/jalview/ws/dbsources/EmblSourceTest.java with 93% similarity]
test/jalview/ws/dbsources/UniprotTest.java
test/jalview/ws/ebi/EBIFetchClientTest.java
test/jalview/ws/gui/Jws2ParamView.java
test/jalview/ws/jabaws/DisorderAnnotExportImport.java
test/jalview/ws/jabaws/RNAStructExportImport.java
test/jalview/ws/jws2/ParameterUtilsTest.java
test/jalview/ws/seqfetcher/DbRefFetcherTest.java
test/junit/extensions/PrivilegedAccessor.java
utils/channels/default/resources/channel.props
utils/channels/develop/images/jalview_develop_getdown_background.png
utils/channels/develop/images/jalview_develop_getdown_background.xcf
utils/channels/develop/images/jalview_develop_getdown_background_error.png
utils/channels/develop/images/jalview_develop_getdown_background_initialising.png
utils/channels/develop/resources/channel.props
utils/channels/jalviewjs/resources/channel.props
utils/channels/release/images/jalview_dmg_DS_Store
utils/channels/release/images/jalview_logo.svg
utils/channels/release/resources/channel.props
utils/channels/test-release/channel_gradle.properties
utils/channels/test-release/images/jalview_logo.icns [deleted file]
utils/channels/test-release/images/jalview_logo.ico [deleted file]
utils/channels/test-release/images/jalview_logo.svg [deleted file]
utils/channels/test-release/images/jalview_test-release_banner.xcf
utils/channels/test-release/images/jalview_test-release_dmg_DS_Store
utils/channels/test-release/images/jalview_test-release_getdown_background.png
utils/channels/test-release/images/jalview_test-release_getdown_background.xcf [moved from utils/channels/test-release/images/jalview_getdown_background.xcf with 98% similarity]
utils/channels/test-release/images/jalview_test-release_getdown_background_error.png
utils/channels/test-release/images/jalview_test-release_getdown_background_initialising.png
utils/channels/test-release/images/jalview_test-release_logo-84.png [new file with mode: 0644]
utils/channels/test-release/images/jalview_test-release_logo-88.png [new file with mode: 0644]
utils/channels/test-release/images/jalview_test-release_logo.icns [new file with mode: 0644]
utils/channels/test-release/images/jalview_test-release_logo.ico [new file with mode: 0644]
utils/channels/test-release/images/jalview_test-release_logo.png [new file with mode: 0644]
utils/channels/test-release/images/jalview_test-release_logo.svg [new file with mode: 0644]
utils/channels/test-release/resources/channel.props
utils/channels/test-release/resources/images/jalview_logo-128.png [deleted file]
utils/channels/test-release/resources/images/jalview_logo-16.png [deleted file]
utils/channels/test-release/resources/images/jalview_logo-256.png [deleted file]
utils/channels/test-release/resources/images/jalview_logo-32.png [deleted file]
utils/channels/test-release/resources/images/jalview_logo-38.png [deleted file]
utils/channels/test-release/resources/images/jalview_logo-48.png [deleted file]
utils/channels/test-release/resources/images/jalview_logo-512.png [deleted file]
utils/channels/test-release/resources/images/jalview_logo-64.png [deleted file]
utils/channels/test-release/resources/images/jalview_test-release_banner.png
utils/channels/test-release/resources/images/jalview_test-release_logo-128.png [new file with mode: 0644]
utils/channels/test-release/resources/images/jalview_test-release_logo-16.png [new file with mode: 0644]
utils/channels/test-release/resources/images/jalview_test-release_logo-256.png [new file with mode: 0644]
utils/channels/test-release/resources/images/jalview_test-release_logo-32.png [new file with mode: 0644]
utils/channels/test-release/resources/images/jalview_test-release_logo-38.png [new file with mode: 0644]
utils/channels/test-release/resources/images/jalview_test-release_logo-48.png [new file with mode: 0644]
utils/channels/test-release/resources/images/jalview_test-release_logo-512.png [new file with mode: 0644]
utils/channels/test-release/resources/images/jalview_test-release_logo-64.png [new file with mode: 0644]
utils/channels/test-release/resources/images/rotatable_jalview_logo-38.png [deleted file]
utils/channels/test-release/resources/images/rotatable_jalview_test-release_logo-38.png [new file with mode: 0644]
utils/create_iconfiles.sh [deleted file]
utils/debian/build_gradle.patch [new file with mode: 0644]
utils/debian/debian/icons/128x128/apps/jalview-icon.png [new file with mode: 0644]
utils/debian/debian/icons/16x16/apps/jalview-icon.png [new file with mode: 0644]
utils/debian/debian/icons/16x16/mimetypes/x-jalview-file.png [new file with mode: 0644]
utils/debian/debian/icons/22x22/mimetypes/x-jalview-file.png [new file with mode: 0644]
utils/debian/debian/icons/24x24/mimetypes/x-jalview-file.png [new file with mode: 0644]
utils/debian/debian/icons/256x256/apps/jalview-icon.png [new file with mode: 0644]
utils/debian/debian/icons/32x32/apps/jalview-icon.png [new file with mode: 0644]
utils/debian/debian/icons/32x32/mimetypes/x-jalview-file.png [new file with mode: 0644]
utils/debian/debian/icons/48x48/apps/jalview-icon.png [new file with mode: 0644]
utils/debian/debian/icons/48x48/mimetypes/x-jalview-file.png [new file with mode: 0644]
utils/debian/debian/icons/512x512/apps/jalview-icon.png [new file with mode: 0644]
utils/debian/debian/icons/512x512/mimetypes/x-jalview-file.png [new file with mode: 0644]
utils/debian/debian/icons/64x64/apps/jalview-icon.png [new file with mode: 0644]
utils/debian/debian/jalview-icon.png [moved from utils/channels/test-release/images/jalview_logo.png with 100% similarity]
utils/debian/debian/jalview-mailcap [new file with mode: 0644]
utils/debian/debian/jalview-mime.xml [new file with mode: 0644]
utils/debian/debian/jalview.desktop [new file with mode: 0644]
utils/debian/debian/wrappers/jalview [new file with mode: 0755]
utils/debian/debian_build.gradle [new file with mode: 0644]
utils/debian/etc/jalview_properties [new file with mode: 0644]
utils/debian/file_associations_template-mailcap.txt [new file with mode: 0644]
utils/debian/file_associations_template-shared-mime-info.xml [new file with mode: 0644]
utils/debian/mime_types_for_debian.pl [new file with mode: 0755]
utils/getdown/bin/jalview.bat [new file with mode: 0755]
utils/getdown/bin/jalview.ps1 [new file with mode: 0755]
utils/getdown/bin/jalview.sh [new file with mode: 0755]
utils/install4j/DS_Store [deleted file]
utils/install4j/DS_Store-DEVELOP [deleted file]
utils/install4j/DS_Store-NON-RELEASE [deleted file]
utils/install4j/DS_Store-TEST-RELEASE [deleted file]
utils/install4j/DS_Store_no_link [deleted file]
utils/install4j/Uninstall Old Jalview.app/Contents/Info.plist [deleted file]
utils/install4j/Uninstall Old Jalview.app/Contents/MacOS/applet [deleted file]
utils/install4j/Uninstall Old Jalview.app/Contents/PkgInfo [deleted file]
utils/install4j/Uninstall Old Jalview.app/Contents/Resources/Scripts/main.scpt [deleted file]
utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.icns [deleted file]
utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.rsrc [deleted file]
utils/install4j/Uninstall Old Jalview.app/Contents/Resources/description.rtfd/TXT.rtf [deleted file]
utils/install4j/Uninstall Old Jalview.app/Contents/_CodeSignature/CodeResources [deleted file]
utils/install4j/Uninstall Old Jalview.scpt [deleted file]
utils/install4j/file_associations_auto-Info_plist.xml
utils/install4j/file_associations_auto-install4j8.xml
utils/install4j/install4j8_template.install4j
utils/jalviewjs/classlists/jvexamplefile.txt
utils/jalviewjs/libjs/jmol-app.zip
utils/mk_jalview_icons.sh [new file with mode: 0755]
utils/osx_signing/README [new file with mode: 0644]
utils/osx_signing/entitlements.txt [new file with mode: 0644]

diff --git a/README b/README
old mode 100755 (executable)
new mode 100644 (file)
index e8c44a9..b374479
--- a/README
+++ b/README
@@ -1,72 +1,5 @@
-Jalview Readme
---------------
+Please see doc/building.md for up to date build and running instructions for the Java desktop application.
 
-
-The source is available as a tar file and comes complete with the GNU General Public License. 
-
-To build the application you will need a J2SDK 1.7+. 
-
-An Ant build file (build.xml) is provided, you will need to install Apache Ant first. 
-Run ant to view usage which will display a list of useful build targets.
-
-Jalview is primarily developed with eclipse, and a .project file is provided to simplify importing the source into your own eclipse workspace. A NetBeans nbbuild.xml file is also provided for developing Jalview with NetBeans - but this is not officially supported.
-
-You may also be able to use Borland JBuilder to build Jalview. A JBuilder project file 
-(JalviewX.jpx, JalviewApplet.jpx) for both application and applet is provided, 
-but the library dependencies are almost certainly out of date. See the build.xml 
-for current dependencies.
-
-##
-
-Jalview-JS
-
-To enable transpilation of Jalview's code:
-
-1. Locate the 'dropins' directory in your eclipse installation and copy swingjs/net.sf.j2s.core.jar to it.
-  - typically it is at the top of the Eclipse installation, or on OSX under Eclipse.app/Contents/Eclipse
-  
-2. Restart Eclipse
-
-3. If all is well you should see the 'Java2Script' builder is listed as the primary builder for the Jalview project.
-  if not, this is because your properties file needs to have the standard java builder replaced with the following:
-  <name>net.sf.j2s.core.java2scriptbuilder</name>
-
-- otherwise Javascript files will now be generated in the site/swingjs/j2s directory whenever a build occurs
-
-4. Execute the 'unzip-to-site' task (if it isn't automatically run) to update the site directory with the latest versions of SwingJS, varna-js, JSmol and other dependencies required by Jalview.
-
-
-  
-
-
-##
-
-For more help, read the file doc/building.html
-
-
-##################
-
-To run application...
-[ NOTE: when using the -classpath option with the '*' wildcard, the argument must be quoted to avoid shell expansion of the wildcard,
-  ALSO, the wildcard MUST be as DIR/* and not DIR/*.jar etc or it will not be interpreted correctly ]
-
-on Windows use:
-  java -classpath "JALVIEW_HOME/lib/*;JALVIEW_HOME/jalview.jar" jalview.bin.Jalview
-and on MacOS or Linux:
-  java -classpath "JALVIEW_HOME/lib/*:JALVIEW_HOME/jalview.jar" jalview.bin.Jalview
-
-Replace JALVIEW_HOME with the full path to Jalview Installation Directory. If building from source:
-
-  java -classpath "JALVIEW_BUILD/dist/*" jalview.bin.Jalview
-
-
-##################
-
-
-If you use a proxy server add 
-
--Dhttp.proxyServer=YOUR.SERVER -Dhttp.proxyPort=YOURPORT
-
-If the proxy server requires authentication, add
-
--Dhttp.proxyUser=USERNAME -Dhttp.proxyPassword=PASSWORD
+JalviewJS.
+See  README_GRADLE_JALVIEWJS-2019-10-22.md  for build instructions for JalviewJS.
+This is a little sparse but enough to do the transpilation.
index fa922d9..c26858d 100644 (file)
@@ -42,7 +42,7 @@ 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
-Jmol-14.6.4_2016.10.26.jar     GPL/LGPLv2 http://sourceforge.net/projects/jmol/files/
+Jmol-14.31.53.jar      GPL/LGPLv2 built manually from commit https://github.com/BobHanson/Jmol-SwingJS/commit/a6a2fb767e3fc2a73e72d926a11fd93a0e4c9f23 (excluded jspecview/application to compile)
 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
index ebb8733..8efe39d 100644 (file)
@@ -40,7 +40,7 @@ plugins {
   id 'eclipse'
   id "com.diffplug.gradle.spotless" version "3.28.0"
   id 'com.github.johnrengelman.shadow' version '4.0.3'
-  id 'com.install4j.gradle' version '8.0.4'
+  id 'com.install4j.gradle' version '8.0.10'
   id 'com.dorongold.task-tree' version '1.5' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
   id 'com.palantir.git-version' version '0.12.3'
 }
@@ -52,6 +52,7 @@ repositories {
 }
 
 
+
 // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
 def string(Object o) {
   return o == null ? "" : o.toString()
@@ -204,7 +205,7 @@ ext {
   install4jDMGDSStore = "${install4j_images_dir}/${install4j_dmg_ds_store}"
   install4jDMGBackgroundImage = "${install4j_images_dir}/${install4j_dmg_background}"
   install4jInstallerName = "${jalview_name} Non-Release Installer"
-  install4jExecutableName = jalview_name.replaceAll("[^\\w]+", "_").toLowerCase()
+  install4jExecutableName = install4j_executable_name
   install4jExtraScheme = "jalviewx"
   install4jMacIconsFile = string("${install4j_images_dir}/${install4j_mac_icons_file}")
   install4jWindowsIconsFile = string("${install4j_images_dir}/${install4j_windows_icons_file}")
@@ -370,6 +371,7 @@ ext {
                                     .replaceAll("_*-_*", "-") // collapse _-_
                                     .toLowerCase()
 
+  getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local"
   getdownAppDir = string("${getdownWebsiteDir}/${getdownAppDistDir}")
   //getdownJ11libDir = "${getdownWebsiteDir}/${getdown_j11lib_dir}"
   getdownResourceDir = string("${getdownWebsiteDir}/${getdown_resource_dir}")
@@ -490,6 +492,7 @@ ext {
   jalviewjsCoreClasslists = []
   jalviewjsJalviewTemplateName = string(jalviewjs_name)
   jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
+  jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}")
   jalviewjsJ2sProps = null
   jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
 
@@ -1367,13 +1370,12 @@ task linkCheck(type: JavaExec) {
   args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
 
   def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
-  def errFOS = outFOS
   standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
     outFOS,
-    standardOutput)
+    System.out)
   errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
     outFOS,
-    errorOutput)
+    System.err)
 
   inputs.dir(helpBuildDir)
   outputs.file(helpLinksCheckerOutFile)
@@ -1408,6 +1410,8 @@ jar {
   def outputDir = "${jalviewDir}/${package_dir}"
   destinationDirectory = file(outputDir)
   archiveFileName = rootProject.name+".jar"
+  duplicatesStrategy "EXCLUDE"
+
 
   exclude "cache*/**"
   exclude "*.jar"
@@ -1473,6 +1477,9 @@ shadowJar {
     attributes "Implementation-Version": JALVIEW_VERSION,
     "Application-Name": install4jApplicationName
   }
+
+  duplicatesStrategy "INCLUDE"
+
   mainClassName = shadow_jar_main_class
   mergeServiceFiles()
   classifier = "all-"+JALVIEW_VERSION+"-j"+JAVA_VERSION
@@ -1571,6 +1578,18 @@ task getdownWebsite() {
         into getdownResourceDir
       }
     }
+    
+    def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
+    getdownWrapperScripts.each{ script ->
+      def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
+      if (s.exists()) {
+        copy {
+          from s
+          into "${getdownWebsiteDir}/${getdown_wrapper_script_dir}"
+        }
+        getdownTextString += "resource = ${getdown_wrapper_script_dir}/${script}\n"
+      }
+    }
 
     def codeFiles = []
     fileTree(file(package_dir)).each{ f ->
@@ -1836,12 +1855,6 @@ task copyInstall4jTemplate {
       }
     }
 
-    // remove the "Uninstall Old Jalview (optional)" symlink from DMG for non-release DS_Stores
-    if (! (CHANNEL == "RELEASE" || CHANNEL == "TEST-RELEASE" ) ) {
-      def symlink = install4jConfigXml.'**'.topLevelFiles.symlink.find { sl -> sl.'@name' == "Uninstall Old Jalview (optional).app" }
-      symlink.parent().remove(symlink)
-    }
-
     // write install4j file
     install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
   }
@@ -1882,6 +1895,8 @@ task installers(type: com.install4j.gradle.Install4jTask) {
     'JALVIEW_APPLICATION_NAME': install4jApplicationName,
     'JALVIEW_DIR': "../..",
     'OSX_KEYSTORE': OSX_KEYSTORE,
+    'OSX_APPLEID': OSX_APPLEID,
+    'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
     'JSIGN_SH': JSIGN_SH,
     'JRE_DIR': getdown_app_dir_java,
     'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
@@ -1903,6 +1918,10 @@ task installers(type: com.install4j.gradle.Install4jTask) {
     'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
     'MACOS_DMG_DS_STORE': install4jDMGDSStore,
     'MACOS_DMG_BG_IMAGE': install4jDMGBackgroundImage,
+    'WRAPPER_LINK': getdownWrapperLink,
+    'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
+    'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
+    'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
     'INSTALLER_NAME': install4jInstallerName,
     'INSTALL4J_UTILS_DIR': install4j_utils_dir,
     'GETDOWN_WEBSITE_DIR': getdown_website_dir,
@@ -1934,15 +1953,25 @@ task installers(type: com.install4j.gradle.Install4jTask) {
   if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
     faster = true
     disableSigning = true
+    disableNotarization = true
   }
 
   if (OSX_KEYPASS) {
     macKeystorePassword = OSX_KEYPASS
+  } 
+  
+  if (OSX_ALTOOLPASS) {
+    appleIdPassword = OSX_ALTOOLPASS
+    disableNotarization = false
+  } else {
+    disableNotarization = true
   }
 
   doFirst {
     println("Using projectFile "+projectFile)
+    if (!disableNotarization) { println("Will notarize OSX App DMG") }
   }
+  //verbose=true
 
   inputs.dir(getdownWebsiteDir)
   inputs.file(install4jConfFile)
@@ -1963,7 +1992,8 @@ spotless {
 task sourceDist(type: Tar) {
   group "distribution"
   description "Create a source .tar.gz file for distribution"
-  
+
+  dependsOn createBuildProperties
   dependsOn convertMdFiles
 
   def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
@@ -2051,6 +2081,15 @@ task sourceDist(type: Tar) {
 //    exclude(EXCLUDE_FILES)
 //    exclude(PROCESS_FILES)
 //  }
+
+  from(file(buildProperties).getParent()) {
+    include(file(buildProperties).getName())
+    rename(file(buildProperties).getName(), "build_properties")
+    filter({ line ->
+      line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
+    })
+  }
+
 }
 
 
@@ -2070,6 +2109,33 @@ task j2sSetHeadlessBuild {
 }
 
 
+task jalviewjsEnableAltFileProperty(type: WriteProperties) {
+  group "jalviewjs"
+  description "Enable the alternative J2S Config file for headless build"
+
+  outputFile = jalviewjsJ2sSettingsFileName
+  def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
+  def j2sProps = new Properties()
+  if (j2sPropsFile.exists()) {
+    try {
+      def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
+      j2sProps.load(j2sPropsFileFIS)
+      j2sPropsFileFIS.close()
+
+      j2sProps.each { prop, val ->
+        property(prop, val)
+      }
+    } catch (Exception e) {
+      println("Exception reading ${jalviewjsJ2sSettingsFileName}")
+      e.printStackTrace()
+    }
+  }
+  if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
+    property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
+  }
+}
+
+
 task jalviewjsSetEclipseWorkspace {
   def propKey = "jalviewjs_eclipse_workspace"
   def propVal = null
@@ -2282,7 +2348,7 @@ task jalviewjsTransferUnzipAllLibs {
 
 task jalviewjsCreateJ2sSettings(type: WriteProperties) {
   group "JalviewJS"
-  description "Create the .j2s file from the j2s.* properties"
+  description "Create the alternative j2s file from the j2s.* properties"
 
   jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
   def siteDirProperty = "j2s.site.directory"
@@ -2301,11 +2367,11 @@ task jalviewjsCreateJ2sSettings(type: WriteProperties) {
       property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
     }
   }
-  outputFile = jalviewjsJ2sSettingsFileName
+  outputFile = jalviewjsJ2sAltSettingsFileName
 
   if (! IN_ECLIPSE) {
     inputs.properties(jalviewjsJ2sProps)
-    outputs.file(jalviewjsJ2sSettingsFileName)
+    outputs.file(jalviewjsJ2sAltSettingsFileName)
   }
 }
 
@@ -2333,6 +2399,10 @@ task jalviewjsSyncAllLibs (type: Sync) {
   preserve {
     include "**"
   }
+
+  // should this be exclude really ?
+  duplicatesStrategy "INCLUDE"
+
   outputs.files outputFiles
   inputs.files inputFiles
 }
@@ -2423,6 +2493,7 @@ task jalviewjsProjectImport(type: Exec) {
   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
   if (!IN_ECLIPSE) {
     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
+    args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
   }
 
   inputs.file("${jalviewDir}/.project")
@@ -2436,6 +2507,9 @@ task jalviewjsTranspile(type: Exec) {
   dependsOn jalviewjsEclipseSetup 
   dependsOn jalviewjsProjectImport
   dependsOn jalviewjsEclipsePaths
+  if (!IN_ECLIPSE) {
+    dependsOn jalviewjsEnableAltFileProperty
+  }
 
   doFirst {
     // do not run a headless transpile when we claim to be in Eclipse
@@ -2455,6 +2529,7 @@ task jalviewjsTranspile(type: Exec) {
   args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
   if (!IN_ECLIPSE) {
     args += [ "-D${j2sHeadlessBuildProperty}=true" ]
+    args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
   }
 
   def stdout
@@ -2482,12 +2557,12 @@ DEBUG: ${eclipseDebug}
         new org.apache.tools.ant.util.TeeOutputStream(
           logOutFOS,
           stdout),
-        standardOutput)
+        System.out)
       errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
         new org.apache.tools.ant.util.TeeOutputStream(
           logErrFOS,
           stderr),
-        errorOutput)
+        System.err)
     } else {
       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
         logOutFOS,
@@ -2575,7 +2650,7 @@ def jalviewjsCallCore(String name, FileCollection list, String prefixFile, Strin
           new org.apache.tools.ant.util.TeeOutputStream(
             logErrFOS,
             stderr),
-          errorOutput)
+          System.err)
     } else {
       standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
         logOutFOS,
@@ -2882,7 +2957,13 @@ task jalviewjsServer {
   def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
   doLast {
 
-    SimpleHttpFileServerFactory factory = new SimpleHttpFileServerFactory()
+    def factory
+    try {
+      def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
+      factory = f.newInstance()
+    } catch (ClassNotFoundException e) {
+      throw new GradleException("Unable to create SimpleHttpFileServerFactory")
+    }
     def port = Integer.valueOf(jalviewjs_server_port)
     def start = port
     def running = false
@@ -2935,7 +3016,7 @@ task cleanJalviewjsAll {
     if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
       delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
     }
-    delete "${jalviewDir}/${jalviewjs_j2s_settings}"
+    delete jalviewjsJ2sAltSettingsFileName
   }
 
   outputs.upToDateWhen( { false } )
similarity index 62%
rename from utils/install4j/README_convert_PNG_to_ICNS_and_ICO
rename to doc/README_convert_PNG_to_ICNS_and_ICO
index cf71c06..11892e0 100644 (file)
@@ -1,4 +1,23 @@
-## Creating the .ico (Windows) and .icns (macOS) files from PNG images in linux
+## There is now a script utils/mk_jalview_icons.sh that creates the .icns and .ico icon files, and also the rotatable icon.
+Run it as
+```
+mk_jalview_icons.sh <BASENAME>-512.png
+```
+Having an existing 512x512 PNG image with the above filename format is important.
+It will then create multiple files
+`<BASENAME>-<N>.png`
+for N being 16, 32, 38, 48, 64, 128, 256.
+**NB** You might want to edit these as described below.  The `mk_jalview_icons.sh` will not overwrite these files, but it
+will overwrite the `.ico` and `.icns` files, so after editing the `<BASENAME>-<N>.png` files, just run the script again.
+
+As below, the script relies on ImageMagick and icnsutils being installed with `convert` and `png2icns` in the PATH.
+
+The rotatable logo will also be created (and overwritten) as
+`rotatable_<BASENAME>-38.png`
+
+
+## Old text
+### Creating the .ico (Windows) and .icns (macOS) files from PNG images in linux
 
 Create multiple square versions of your icon at the following resolutions:
 16x16, 32x32, 48x48, 64x64, 128x128, 256x256, 512x512
@@ -25,7 +44,7 @@ as png2icns complains if it finds one!
 
 This icon is used by the web services progress window, with the rotating Jalview logo.
 It requires some small margin of white pixels around the logo to enable antialiasing around the edge of the logo.
-You can make one form a 48x48 transparent logo with
+You can make one form a 38x38 transparent logo with
 ```
 convert jalview_logo-38.png -gravity center -background white -extent 60x60 rotatable_jalview_logo-38.png
 ```
diff --git a/doc/old_README b/doc/old_README
new file mode 100755 (executable)
index 0000000..e8c44a9
--- /dev/null
@@ -0,0 +1,72 @@
+Jalview Readme
+--------------
+
+
+The source is available as a tar file and comes complete with the GNU General Public License. 
+
+To build the application you will need a J2SDK 1.7+. 
+
+An Ant build file (build.xml) is provided, you will need to install Apache Ant first. 
+Run ant to view usage which will display a list of useful build targets.
+
+Jalview is primarily developed with eclipse, and a .project file is provided to simplify importing the source into your own eclipse workspace. A NetBeans nbbuild.xml file is also provided for developing Jalview with NetBeans - but this is not officially supported.
+
+You may also be able to use Borland JBuilder to build Jalview. A JBuilder project file 
+(JalviewX.jpx, JalviewApplet.jpx) for both application and applet is provided, 
+but the library dependencies are almost certainly out of date. See the build.xml 
+for current dependencies.
+
+##
+
+Jalview-JS
+
+To enable transpilation of Jalview's code:
+
+1. Locate the 'dropins' directory in your eclipse installation and copy swingjs/net.sf.j2s.core.jar to it.
+  - typically it is at the top of the Eclipse installation, or on OSX under Eclipse.app/Contents/Eclipse
+  
+2. Restart Eclipse
+
+3. If all is well you should see the 'Java2Script' builder is listed as the primary builder for the Jalview project.
+  if not, this is because your properties file needs to have the standard java builder replaced with the following:
+  <name>net.sf.j2s.core.java2scriptbuilder</name>
+
+- otherwise Javascript files will now be generated in the site/swingjs/j2s directory whenever a build occurs
+
+4. Execute the 'unzip-to-site' task (if it isn't automatically run) to update the site directory with the latest versions of SwingJS, varna-js, JSmol and other dependencies required by Jalview.
+
+
+  
+
+
+##
+
+For more help, read the file doc/building.html
+
+
+##################
+
+To run application...
+[ NOTE: when using the -classpath option with the '*' wildcard, the argument must be quoted to avoid shell expansion of the wildcard,
+  ALSO, the wildcard MUST be as DIR/* and not DIR/*.jar etc or it will not be interpreted correctly ]
+
+on Windows use:
+  java -classpath "JALVIEW_HOME/lib/*;JALVIEW_HOME/jalview.jar" jalview.bin.Jalview
+and on MacOS or Linux:
+  java -classpath "JALVIEW_HOME/lib/*:JALVIEW_HOME/jalview.jar" jalview.bin.Jalview
+
+Replace JALVIEW_HOME with the full path to Jalview Installation Directory. If building from source:
+
+  java -classpath "JALVIEW_BUILD/dist/*" jalview.bin.Jalview
+
+
+##################
+
+
+If you use a proxy server add 
+
+-Dhttp.proxyServer=YOUR.SERVER -Dhttp.proxyPort=YOURPORT
+
+If the proxy server requires authentication, add
+
+-Dhttp.proxyUser=USERNAME -Dhttp.proxyPassword=PASSWORD
index b3590a7..d0fb57d 100644 (file)
@@ -123,6 +123,7 @@ install4j_info_plist_file_associations = file_associations_auto-Info_plist.xml
 install4j_installer_file_associations = file_associations_auto-install4j8.xml
 #install4j_DMG_uninstaller_app_files = uninstall_old_jalview_files.xml
 install4j_build_dir = build/install4j
+install4j_executable_name = jalviewg
 install4j_media_types = windows,macosArchive,unixArchive,unixInstaller
 install4j_faster = false
 install4j_application_categories = Science;Biology;Java;
@@ -133,10 +134,16 @@ install4j_png_icon_file = jalview_logo.png
 install4j_background = jalview_logo_background_fade-640x480.png
 install4j_dmg_background = jalview_dmg_background-NON-RELEASE.png
 install4j_dmg_ds_store = jalview_dmg_DS_Store
+getdown_wrapper_script_dir = bin
+getdown_bash_wrapper_script = jalview.sh
+getdown_powershell_wrapper_script = jalview.ps1
+getdown_batch_wrapper_script = jalview.bat
 
 OSX_KEYSTORE =
 OSX_KEYPASS =
 JSIGN_SH = echo
+OSX_APPLEID =
+OSX_ALTOOLPASS =
 
 CHANNEL=LOCAL
 getdown_channel_base = https://www.jalview.org/getdown
@@ -158,6 +165,19 @@ eclipse_project_name = jalview
 eclipse_bin_dir = bin
 eclipse_debug = false
 
+## IMPORTANT for headless build
+# to enable `gradle jalviewjs` headless build you MUST set
+# j2s.config.altfileproperty = org.jalview.jalviewjsj2sfilename
+# (or set to whatever value jalviewjs_j2s_alt_file_property is set to)
+# in your ./.j2s file
+# OR run the task `gradle jalviewjsEnableAltFileProperty` to do that for you (before headless build task)
+jalviewjs_j2s_alt_file_property = org.jalview.jalviewjsj2sfilename
+jalviewjs_j2s_alt_settings = build/jalviewjs/config/j2s
+
+# these are used for actual .j2s file to pass on to alt j2s file
+jalviewjs_j2s_settings = .j2s
+jalviewjs_j2s_alt_file_property_config = j2s.config.altfileproperty
+
 # for developing in Eclipse as IDE, set this to automatically copy current swingjs/net.sf.j2s.core.jar to your dropins dir
 jalviewjs_eclipseIDE_auto_copy_j2s_plugin = false
 # Override this in a local.properties file
@@ -170,7 +190,6 @@ jalviewjs_libjs_dir = utils/jalviewjs/libjs
 jalviewjs_site_resource_dir = utils/jalviewjs/site-resources
 jalviewjs_classlists_dir = utils/jalviewjs/classlists
 jalviewjs_classlist_jalview = utils/jalviewjs/_j2sclasslist.txt
-jalviewjs_j2s_settings = .j2s
 #jalviewjs_eclipse_workspace = ~/tmp/eclipse-workspace
 # these 3 files/dirs found in build/jalviewjs
 jalviewjs_eclipse_tmp_dropins_dir = eclipse_dropins
index 99d010d..7dbb76d 100755 (executable)
    
    <mapID target="importvcf" url="html/features/importvcf.html" />
    <mapID target="importvcf.attribs" url="html/features/importvcf.html#attribs" />
+   <mapID target="logging" url="html/logging.html" />
 </map>
index a0c7fe6..a72f5ac 100755 (executable)
@@ -27,7 +27,7 @@
         <tocitem text="Virtual Features in CDS/Protein Views" target="splitframe.virtualfeats"/>
                                <tocitem text="VCF Variant Attributes" target="importvcf.attribs"/>
                                <tocitem text="Feature Filters and Attribute Colourschemes" target="features.featureschemes" />
-                               
+        <tocitem text="The Java Console, Logging and Reporting Bugs" target="logging" />
                </tocitem>
                
                <tocitem text="Editing Alignments" target="edit" />
                        </tocitem>
                </tocitem>
                <tocitem text="Preferences" target="preferences" />
+    <tocitem text="The Java Console, Logging and Reporting Bugs" target="logging" />
                <tocitem text="Scripting with Groovy" target="groovy">
                        <tocitem text="Groovy Features Counter example" target="groovy.featurescounter"/>
                </tocitem>
index fcdb908..3517929 100755 (executable)
@@ -189,6 +189,7 @@ GRAPHLINE&#9;<em>graph_name</em>&#9;<em>value</em>&#9;<em>label</em>&#9;<em>colo
         available in Jalview applet due to AWT 1.1 limitations</em>)</li>
   </ul>
   </p>
+  <hr />
   <p>
     <strong><a name="groupdefs">SEQUENCE_GROUP</a></strong><br />
     Groups of sequences and column ranges can be defined using a tab
@@ -199,7 +200,8 @@ GRAPHLINE&#9;<em>graph_name</em>&#9;<em>value</em>&#9;<em>label</em>&#9;<em>colo
   <p>The sequences can be defined by alignment index and a range of
     sequences can be defined in a comma delimited field such as</p>
   <p>2-5,8-15,20,22</p>
-  <p>Enter * to select all groups.</p>
+  <p>Enter * to select all sequences.</p>
+  <p>Set both <em>Group_Start</em> and <em>Group_End</em> to * to include the full sequence(s) range.
   <p>
     <strong>Note:</strong> If the alignment indices are not known, enter
     -1, followed by a tab and then a tab delimited list of sequence IDs.
index 0d800cf..1eecfb9 100644 (file)
   parameters, then include it at the beginning of the command line to
   ensure they are processed before any remaining arguments.
   <br>
+  Typical command line execution follows the following pattern:
+  <pre>
+  jalview -open &lt;Alignment File/URL&gt; [additional import arguments] [export arguments]
+  </pre>
+  
   <table width="100%" border="1" cellspacing="0" cellpadding="0">
     <tr>
       <td width="27%"><div align="center">-nodisplay</div></td>
           User Interface. (automatically disables questionnaire, version
           and usage stats checks)</div></td>
     </tr>
-
+    <tr>
+      <td><div align="center">-open FILE/URL</div></td>
+      <td><div align="left">Specify the alignment file to
+          open or process by providing additional arguments.</div></td>
+    </tr>
     <tr>
       <td><div align="center">-props FILE/URL</div></td>
       <td><div align="left">Use the given Jalview properties
index e00d390..0ef78f9 100644 (file)
@@ -38,7 +38,7 @@
 
   <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>
+       /PATH_TO_JALVIEW/Jalview -open https://www.jalview.org/examples/jpred_msa.fasta -annotations https://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>
@@ -65,10 +65,31 @@ open /Applications/Jalview.app --args -open ~/myalignment.fa</pre><em>(put
   </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>.
+    to modify parameters for Jalview's Java Virtual Machine, or
+    configure system properties, then take a look at the instructions
+    for how to <a href="../memory.html#jvm">setting the JVM's
+      maximum memory</a>.<br /> 
+  <p>
+    <strong>Changing Jalview's 'Look and Feel'</strong> <br />If you
+    are experiencing issues with the font size or layout of Jalview's
+    GUI, you can try changing Jalview's 'Look and feel' by
+    specifying a custom system property 'laf' on startup (see <a
+      href="../memory.html#jvm">setting the JVM's memory</a> for
+    instructions on how to do this for your platform). <br />For the
+    Jalview standalone executable jar, simply provide one of the
+    property settings before the -jar argument
   </p>
+  <ul>
+    <li>-Dlaf=system (default look and feel for the OS)</li>
+    <li>-Dlaf=crossplatform (Java's Metal Look and Feel)</li>
+    <li>-Dlaf=nimbus (Java's alternative Nimbus Look and Feel)</li>
+    <li>-Dlaf=mac (only has an effect on OSX)</li>
+    <li>-Dlaf=gtk (only has an effect on Linux)</li>
+  </ul>
+  The currently configured look and feel is logged to Jalview's console.
+  Once the look and feel has been changed, it will be stored in
+  Jalview's .jalview_properties file for future Jalview sessions.
+
   <p>&nbsp;</p>
   <p>&nbsp;</p>
 </body>
index ead4436..cc91154 100644 (file)
@@ -39,7 +39,7 @@
     installation of Groovy. Just select <strong>Tools&#8594;Groovy
       Console...</strong> from the Jalview Desktop's drop-down menu. After a
     short pause, you should then see the <a
-      href="http://groovy-lang.org/groovyconsole.html">Groovy
+      href="https://groovy-lang.org/groovyconsole.html">Groovy
       Console</a> appear. This allows you to interactively execute Groovy
     scripts whilst Jalview is running. We've also provided a <strong>Calculations&#8594;Execute
       Groovy Script</strong> button so you can execute the currently loaded
@@ -101,7 +101,7 @@ print currentAlFrame.getTitle();</pre>
     InstallAnywhere version of Jalview, you can find additional groovy
     scripts in the examples/groovy subfolder of the installation
     directory. The examples are also available at <a
-      href="http://www.jalview.org/examples/groovy">http://www.jalview.org/examples/groovy</a>.
+      href="https://www.jalview.org/examples/groovy">https://www.jalview.org/examples/groovy</a>.
   </p>
   <p>
     <em>Using Groovy to add new Alignment Calculations</em><br />We've
@@ -115,8 +115,8 @@ print currentAlFrame.getTitle();</pre>
     <em>Creating custom colourschemes</em><br/>
     You can create your own alignment colourschemes with a groovy script. We've provided two examples:<br/>
     <ul>
-    <li><a href="http://www.jalview.org/examples/groovy/colourConserved.groovy">colourConserved.groovy</a> creates an 'Conserved' colourscheme - similar to the classic <a href="http://www.nrbsc.org/old/gfx/genedoc/">GeneDOC</a> shading model.</li>
-    <li><a href="http://www.jalview.org/examples/groovy/colourUnconserved.groovy">colourUnconserved.groovy</a> creates an 'Unconserved' colourscheme, where any unconserved residues are coloured pink.</li>
+    <li><a href="https://www.jalview.org/examples/groovy/colourConserved.groovy">colourConserved.groovy</a> creates an 'Conserved' colourscheme - similar to the classic <a href="http://www.nrbsc.org/old/gfx/genedoc/">GeneDOC</a> shading model.</li>
+    <li><a href="https://www.jalview.org/examples/groovy/colourUnconserved.groovy">colourUnconserved.groovy</a> creates an 'Unconserved' colourscheme, where any unconserved residues are coloured pink.</li>
     
     </ul>
   </p>
index 27742b3..e23af30 100644 (file)
   <pre>
 # Jalview Launch File
 # Please install the Jalview Desktop from 
-# http://www.jalview.org/getdown/release
+# https://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=https://www.jalview.org/examples/jpred_msa.fasta
 jalview.apparg=-annotations
-jalview.apparg=http://www.jalview.org/examples/jpred_msa.seq.concise
+jalview.apparg=https://www.jalview.org/examples/jpred_msa.seq.concise
 jalview.apparg=-colour
 jalview.apparg=Clustal
 </pre>
@@ -62,7 +62,7 @@ jalview.apparg=Clustal
 # 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/
+appbase=https://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.
index 58b06db..5a3fe7a 100755 (executable)
     <em>Add Temperature Factor annotation to alignment</em> - if
     selected, values extracted from the Temperature Factor column for
     the backbone atoms in the PDB file will be extracted as annotation
-    lines shown on the alignment.
+    lines shown on the alignment.<br/><em>Since 2.11.2, scores from the Temperature Column for structures imported via the 3D-Beacons network may be shown instead as model quality or reliability scores.</em>
   <p>
-    <em>Default structure viewer</em> - choose Jmol or CHIMERA for
+    <em>Default structure viewer</em> - choose Jmol, CHIMERA, CHIMERAX or PYMOL for
     viewing 3D structures.
   <p>
-    <em>Path to Chimera program</em> - Optional, as Jalview will search
-    standard installation paths for Windows, Linux or MacOS. If you have
-    installed Chimera in a non-standard location, you can specify it
-    here, by entering the full path to the Chimera executable program.
-    Double-click this field to open a file chooser dialog.
-  <p>
+    <em>Path to Chimera/X/Pymol program</em> - Optional, as Jalview will search
+    standard installation paths for Windows, Linux or MacOS. If Jalview cannot locate the installation for your selected structure viewer, a dialog will be shown. If you have
+    installed the chosen viewer in a non-standard location, you can specify it
+    here, by entering the full path to its executable.<br/>For Chimera, locate the path to the chimera program, similarly for ChimeraX and Pymol. Rather than typing in the path, you can also <em>double-click this field</em> 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
index eec68ee..837d7b3 100755 (executable)
@@ -36,7 +36,7 @@ td {
   </p>
   <p>The search box is displayed by pressing Control and F or
     selecting &quot;Find...&quot; from the &quot;Search&quot; menu.</p>
-  <img src="search.png" width="398" height="124">
+  <img src="search.png" width="400" height="152">
   <p>&quot;Find next&quot; will find the next occurrence of the
     specified and adjust the alignment window view to show it, and
     &quot;Find all&quot; highlights all matches for a pattern. The
@@ -48,18 +48,19 @@ td {
       of posix and perl style regex - see below for a summary)</li>
     <li>Gaps are ignored when matching the query to the sequences
       in the alignment.</li>
+    <li>Hidden columns can optionally be ignored (<em>since Jalview 2.11</em>)</li>
     <li>The search is applied to both sequences and their IDs, and
       optionally also to the description string (<em>since Jalview
         2.10</em>)
     </li>
     <li>If a region is selected, then search will <strong>only</strong>
-      be performed on that region.
+      be performed on that region.<br />
+    <em>Tip: to quickly clear the current selection, click the
+        alignment view you wish to search, then press 'Escape'.</em>
     </li>
-    <li>To quickly clear the current selection, press the
-      &quot;Escape&quot; key.</li>
     <li>Tick the &quot;Match Case&quot; box to perform a case
       sensitive search.</li>
-    <li>To access a <a ref="#queryhistory">previously used
+    <li>To access a <a href="#queryhistory">previously used
         query</a> press the down arrow or click on the button on the right
       of the text field.
   </ul>
@@ -155,12 +156,13 @@ td {
     stored along with your Jalview user preferences. To open the search
     history, click on the button to the right of the query field, or
     press the down arrow key.</p>
-  <img src="searchhist.png" width="404" height="185" align="left" />
-  <p>The search history keeps up to 99 queries by default. To clear
+  <p><img src="searchhist.png" width="404" height="185" />
+  </p><p>The search history keeps up to 99 queries by default. To clear
     the history, or modify the size of the history, right-click the text
     box.</p>
-  <img src="searchclearhist.png" width="402" height="127" align="left" />
-  <p>
+  <p><img src="searchclearhist.png" width="402" height="127"/>
+  </p>
+<p width="100%">
     <strong>Other dialogs that provide a query history</strong>
   </p>
   <p>
index 89adb1a..47c18f4 100644 (file)
Binary files a/help/help/html/features/search.png and b/help/help/html/features/search.png differ
index e726c49..4e9a75f 100755 (executable)
       &quot;OK&quot; to initiate the retrieval.</li>
   </ol>
 
-  <p>If you use the WSDBFetch sequence fetcher services (EMBL,
-    UniProt, PFAM, and RFAM) in work for publication, please cite:</p>
-  <p>
-    Pillai S., Silventoinen V., Kallio K., Senger M., Sobhany S., Tate
-    J., Velankar S., Golovin A., Henrick K., Rice P., Stoehr P., Lopez
-    R. <br> SOAP-based services provided by the European
-    Bioinformatics Institute.<br> Nucleic Acids Res. 33(1):W25-W28
-    (2005) <br> <br>
-  </p>
+  <p>If you use the Sequence Fetcher, please remember to cite the
+    corresponding services (linked to below):</p>
+  <ul>
+    <li>Ensembl - <a
+      href="https://github.com/Ensembl/ensembl-rest/wiki#citing">The
+        Ensembl REST API</a></li>
+    <li>EMBL/EMBLCDS - Provided by the <a
+      href="https://www.ebi.ac.uk/ena/browser/api/#/ENA_Browser_Data_API/getFlatFileUsingGET">European
+        Nucleotide Archive's ENA Data API</a><br />
+    <em>Note: Versions of Jalview prior to 2.11.1.1 employed the
+        XML endpoint of the ENA browser, which was retired in August
+        2020.</em></li>
+    <li>Uniprot - Free Text Search and Retrieval via the <a
+      href="https://www.uniprot.org/help/api">Uniprot REST API</a></li>
+    <li>PDB - Free Text Search via the <a
+      href="https://www.ebi.ac.uk/pdbe/api/doc/search.html">PDBe
+        REST API</a> and retrieval via <a
+      href="https://www.ebi.ac.uk/Tools/dbfetch/">WSDbFetch</a><br />
+      Pillai S., Silventoinen V., Kallio K., Senger M., Sobhany S., Tate
+      J., Velankar S., Golovin A., Henrick K., Rice P., Stoehr P., Lopez
+      R. <br> SOAP-based services provided by the European
+      Bioinformatics Institute.<br> Nucleic Acids Res.
+      33(1):W25-W28 (2005) <br> <br>
+    </li>
+  </ul>
 </body>
 </html>
index 3b6705b..bd7144e 100644 (file)
@@ -43,7 +43,7 @@
   </ol>
   <strong>Please note: The 2.10.2 feature counting interface is not compatible with earlier versions.</strong><br/><br/>
   <em><a
-    href="http://www.jalview.org/examples/groovy/featuresCounter.groovy">http://www.jalview.org/examples/groovy/featuresCounter.groovy</a>
+    href="https://www.jalview.org/examples/groovy/featuresCounter.groovy">https://www.jalview.org/examples/groovy/featuresCounter.groovy</a>
     - rendered with <a href="http://hilite.me">hilite.me</a></em>
   <!-- HTML generated using hilite.me --><div style="background: #f8f8f8; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #408080; font-style: italic">/*</span>
 <span style="color: #408080; font-style: italic"> * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)</span>
index 442c508..a6bb1b6 100755 (executable)
@@ -45,7 +45,7 @@
   <p>
     For more information, you might also want to take a look at the
     documentation section of the Jalview website (<a
-      href="http://www.jalview.org/about/documentation">http://www.jalview.org/about/documentation</a>).
+      href="https://www.jalview.org/about/documentation">https://www.jalview.org/about/documentation</a>).
   </p>
   <p>
     If you are using the Jalview Desktop application and are looking for
     google the online version of these pages. If you don't find what you
     are looking for, or want to report a bug or make a feature request,
     then get in contact over at <a
-      href="http://www.jalview.org/community">http://www.jalview.org/community</a>
+      href="https://www.jalview.org/community">https://www.jalview.org/community</a>
+  </p>
+  <p>
+    <strong>Logging, troubleshooting and reporting bugs</strong><br />If
+    something seems to be wrong with your Jalview installation, or you
+    think you've found a problem, take a look at <a href="logging.html">Jalview's
+      logging and bug reporting</a> documentation.
   </p>
-
   <p>
     <strong>Citing Jalview</strong><br />If you use Jalview in your
     work, please cite the Jalview 2 paper in Bioinformatics:
index 29b6813..0faa1d5 100755 (executable)
       <td><strong>Cursor Keys<br> (Arrow Keys)
       </strong></td>
       <td>Cursor</td>
-      <td>Move cursor around alignment</td>
+      <td>Move cursor around alignment.<br /> Press SHIFT to move
+        cursor from an aligned region to next gap, or to the next
+        aligned region when at a gap.
+      </td>
     </tr>
     <tr>
       <td><strong>Cursor Keys<br> (Arrow Keys)
diff --git a/help/help/html/logging.html b/help/help/html/logging.html
new file mode 100644 (file)
index 0000000..0975a47
--- /dev/null
@@ -0,0 +1,212 @@
+<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>The Java Console, Logging and Reporting Bugs</title>
+</head>
+<body>
+  <p>
+    <strong>The Java Console, Logging and Reporting Bugs<br /></strong>
+  </p>
+  <p>
+    Like most programs, Jalview contains bugs, despite our best efforts.
+    However, Jalview also produces a series of messages during its
+    operation, often referred to as 'logs'. These logs provide a record
+    of Jalview's operation. They can also be extremely useful when <a
+      href="#reportingbugs">reporting bugs</a>, since they help the
+    Jalview developers diagnose and find a workaround for specific
+    problems that you might encounter.
+  </p>
+  <p>
+    The primary place to look for logs is in the <a href="#java_console">Java
+      Console</a> which you can open from within Jalview by going to the <em>Tools</em>
+    menu and checking the box next to <em>Show Java Console</em>. This
+    option is stored in your Jalview preferences file and so is
+    remembered across Jalview sessions.
+  </p>
+  <p>The Java Console will show you information about what the
+    Jalview application is doing (often in the background) whilst it is
+    running.</p>
+  <p>However, when tracking down problems preventing Jalview from
+    starting up properly, you need to look at the startup logs - which
+    are not shown in the Jalview Console. The location of these depends
+    on how you launched Jalview:</p>
+  <p>
+    <strong>Jalview Desktop Installation Launch Logs</strong><br />If you are using
+    a standard desktop version of Jalview installed from one of our
+    install4j installers, then messages about Jalview's initial launch
+    can be found in
+  <pre>JALVIEW_APP_DIR/launcher.log</pre>
+  where
+  <em>JALVIEW_APP_DIR</em> is the directory that Jalview's application
+  was installed into.
+  <br /> For Jalview 2.11.0 onwards:
+  <ul>
+    <li>In Windows this is <em>%APPDATA%\Local\Jalview</em> by
+      default
+    </li>
+    <li>In macOS this is <em>/Applications/Jalview.app/Contents/Resources/app</em>
+      by default
+    </li>
+    <li>In Linux and other Unix OSes this is <em>~/opt/jalview</em>
+      by default
+    </li>
+  </ul>
+  <p><strong>Jalview Executable Jar Launch Logs</strong><br/>If you are using the Jalview executable jar file (also
+  used by bioconda and OSX homebrew installations) then the default run class (
+  <em>jalview.bin.Launcher</em> -- a minimised launcher that will set
+  memory and linux dpi settings before re-launching
+  <em>jalview.bin.Jalview</em>), will output logging information to
+  STDOUT and STDERR.
+  </p>
+
+  <p><strong>
+    <a name="java_console">Java Console and Log Level</a>
+  </strong></p>
+  <p>
+    The Java Console is opened by selecting <strong>Tools
+      &rarr; Show Java Console</strong>. The visibility of the console is stored
+    in your preferences, so if you quit Jalview with the console open,
+    it will be shown the next time you start Jalview. You can close the
+    console by selecting the same menu option again, or just closing the
+    console window.
+  </p>
+  <p>The Java Console's text display always shows information about
+    your system and Jalview installation details. The rest are the most
+    recent messages output during your Jalview session. Some messages
+    are only captured by the console when it is open, so to get a full
+    log for debugging a problem, enable the console and then restart
+    Jalview.</p>
+
+  <p>
+    You can temporarily control the detail of what appears as output by
+    selecting a <em>Log level</em> using the drop-down list at the
+    bottom left of the console. There are several levels to choose from:
+    The most verbose is TRACE, followed by DEBUG, INFO, WARN. When the
+    Console is opened, the default level will be chosen (INFO).
+  </p>
+  <p>
+    <strong>Note! If you change the log level in the Java
+      Console, this change will only persist for as long as the console
+      is open. Once you close the console the log level will revert back
+      to what it had been when you opened the console (usually INFO).</strong>
+  </p>
+  <p><strong>Permanently changing Jalview's default log level</strong><br/>
+    You can change the default log level by editing the Jalview
+    preferences file, <em>.jalview_properties</em>, found in your home
+    directory (on Windows: %HOMEPATH%, or the folder above 'My
+    Documents'; on macOS: ~ or /Users/<em>username</em>; on linux/unix:
+    ~ or /home/<em>username</em>), and setting the property <em>logs.Jalview.level</em>
+    to the log level you prefer, e.g.
+  <pre>
+  logs.Jalview.level=DEBUG
+  </pre>
+  You can also set the property
+  <pre>
+  logs.Axis.level=DEBUG
+  </pre>
+  <p>to get debug information for Jalview's JPred service. The Axis log
+  level cannot be set from within the Java Console.
+  </p>
+  <p>
+    You can also set the <em>logs.jalview.level</em> property to a log level
+    not usually presented in the Java Console (though restricted to log
+    levels used by Apache Log4j -- see <a
+      href="https://logging.apache.org/log4j/2.x/manual/customloglevels.html">Log4j
+      Custom Log Levels</a> for details of the standard log levels
+    available). Jalview does not currently define any custom log levels.
+    If you do set the property with a log level that is normally not
+    visible in the Java Console this should be respected and visibly
+    selected when you open the console.
+  </p>
+  <p>
+    The <em>Clear</em> button at the bottom of the console will clear
+    all logging messages except for the initial system information which
+    is rewritten to the console.
+  </p>
+  <p>
+    The <em>Copy to clipboard</em> button at the bottom right of the
+    console will copy all of the text in the console to your system
+    clipboard, ready to paste into another application (e.g. email
+    composer or issue tracker).
+  </p>
+
+  <p><strong><a name="reportingbugs">Reporting Bugs</a></strong></p>
+
+  <p>
+    If you come across a problem in Jalview where something is not
+    working as described, or how you think it should, you should first
+    check the <a href="https://www.jalview.org/faq">Jalview FAQ</a> to
+    see if this is a known problem and if there is a suggested
+    workaround.
+  </p>
+  <p>
+    If there is no FAQ answer covering your problem then you can submit
+    a bug report on the <a href="https://issues.jalview.org/">Jalview
+      Issue Tracker</a>. It is good practice to search the issue tracker
+    first to see if the issue has already been reported. If an issue
+    already exists please continue to add your own comments to the issue
+    which may well help narrow down the problem, if not then you can
+    create an account and submit a new bug report:
+  </p>
+  <p>
+    Make sure that you set Project to <em>Jalview (JAL)</em>, and Issue
+    Type to <em>Bug</em> or <em>New Feature</em> or <em>Improvement</em>
+    appropriately.<br /> Give a one line summary of the issue in the <em>Summary</em>.
+    <br /> In the <em>Environment</em> text box you can describe the
+    system you are using. This is usually most easily done by opening
+    the Java Console, clicking the <em>Clear</em> button, and then
+    immediately on the <em>Copy to clipboard</em> button, and then
+    pasting the clipboard into the text box.
+  </p>
+  <p>
+    You can then give more detailed information about how to recreate
+    the problem in the <em>Description</em> text box. If you want to
+    attach any screenshots or example alignment files that demonstrate
+    the problem then you can drag them to the Create Issue dialog in
+    your browser, or use the <em>Attachment</em> browse facility to
+    locate them on your computer.
+  </p>
+
+  <p>
+    To help the Jalview team with diagnosing a particular issue, it is
+    really helpful if you can also add more detailed logs output whilst
+    re-creating the problem. To do this, open the Java Console, click
+    the <em>Clear</em> button and select TRACE in the <em>Log level</em>
+    drop down list. <br /> Whilst leaving the console open, perform the
+    task in Jalview that re-creates the problem. <br /> Then you can
+    copy the debug information in the Java Console by clicking on the <em>Copy
+      to clipboard</em> button and then paste that into the Description, or a
+    Comment of your issue.
+  </p>
+
+  <p>
+    For other queries or comments about Jalview, remember you can
+    contact the Jalview team using email via the
+    <a href="https://www.jalview.org/mailman/listinfo/jalview-discuss">Jalview
+      discussion list</a>, on Twitter <a
+      href="https://twitter.com/Jalview/">@Jalview</a>, or for technical
+    discussions, via the Jalview developer's chatroom at
+    <a href="https://gitter.im/jalview/developers">https://gitter.im/jalview/developers</a>.
+  </p>
+
+</body>
+</html>
index 0374dcb..7d6fb85 100755 (executable)
@@ -80,7 +80,7 @@
       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
+       /PATH_TO_JALVIEW/Jalview /path/to/file/mymemorysetting.jvl -open https://www.jalview.org/examples/jpred_msa.fasta -annotations https://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>
index c83741a..fe4d257 100755 (executable)
@@ -1,3 +1,4 @@
+
 <html>
 <!--
  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
@@ -51,23 +52,351 @@ li:before {
   </p>
   <table border="1">
     <tr>
-      <th nowrap><em>Release</em></th>
+      <th nowrap><a id="Jalview.$$Version-Rel$$"><em>Release</em></th>
       <th><em>New Features</em></th>
       <th><em>Issues Resolved</em></th>
     </tr>
     <tr>
       <td width="60" align="center" nowrap><strong><a
+          id="Jalview.2.11.2">2.11.2</a><a id="Jalview.2.11.2.0">.0</a><br />
+          <em>29/09/2021</em></strong></td>
+      <td align="left" valign="top">
+       <ul>
+         <li><!-- --></li>
+       </ul>
+       <em>Development</em>
+        <ul>
+          <li>Updated building instructions</li>
+        </ul></td>
+      <td>
+        <ul>
+          <li>
+            <!-- JAL-3840 -->Occupancy calculation is incorrect for
+            alignment columns with over -1+2^32 gaps (breaking filtering
+            and display)
+          </li>
+          <li>
+            <!-- JAL-3833 -->Caps on Hi-DPI scaling to prevent crazy
+            scale factors being set with buggy window-managers (linux
+            only)
+          </li>
+        </ul> <em>Development</em>
+        <ul>
+          <li>Fixed non-fatal gradle errors during build</li>
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" align="center" nowrap><strong><a
+          id="Jalview.2.11.1">2.11.1</a><a id="Jalview.2.11.1.4">.4</a><br />
+          <em>09/03/2021</em></strong></td>
+      <td align="left" valign="top"><em>Improved control of
+          Jalview's use of network services via jalview_properties</em>
+        <ul>
+          <li>
+            <!-- JAL-3814 -->New .jalview_properties token controlling
+            launch of the news browser (like -nonews argument)
+          </li>
+          <li>
+            <!-- JAL-3813 -->New .jalview_properties token controlling
+            download of linkout URLs from
+            www.jalview.org/services/identifiers
+          </li>
+          <li>
+            <!-- JAL-3812 -->New .jalview_properties token controlling
+            download of BIOJSHTML templates
+          </li>
+          <li>
+            <!-- JAL-3811 -->New 'Discover Web Services' option to
+            trigger a one off JABAWS discovery if autodiscovery was
+            disabled
+          </li>
+        </ul></td>
+      <td align="left" valign="top">
+        <ul>
+          <li>
+            <!-- JAL-3818 -->Intermittent deadlock opening structure in
+            Jmol
+          </li>
+        </ul> <em>New Known defects</em>
+        <ul>
+          <li>
+            <!-- JAL-3705 -->Protein Cross-Refs for Gene Sequence not
+            always restored from project (since 2.10.3)
+          </li>
+          <li>
+            <!-- JAL-3806 -->Selections from tree built from CDS aren't
+            propagated to Protein alignment (since 2.11.1.3)
+          </li>
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" align="center" nowrap><strong><a
+          id="Jalview.2.11.1">2.11.1</a><a id="Jalview.2.11.1.3">.3</a><br />
+          <em>29/10/2020</em></strong></td>
+      <td align="left" valign="top">
+        <ul>
+
+        </ul>
+      </td>
+      <td align="left" valign="top">
+        <ul>
+          <li>
+            <!-- JAL-3765 -->Find doesn't always highlight all matching
+            positions in a sequence (bug introduced in 2.11.1.2)
+          </li>
+          <li>
+            <!-- JAL-3760 -->Alignments containing one or more protein
+            sequences can be classed as nucleotide
+          </li>
+          <li>
+            <!-- JAL-3748 -->CDS alignment doesn't match original CDS
+            sequences after alignment of protein products (known defect
+            first reported for 2.11.1.0)
+          </li>
+          <li>
+            <!-- JAL-3725 -->No tooltip or popup menu for genomic
+            features outwith CDS shown overlaid on protein
+          </li>
+          <li>
+            <!-- JAL-3751 -->Overlapping CDS in ENA accessions are not
+            correctly mapped by Jalview (e.g. affects viral CDS with
+            ribosomal slippage, since 2.9.0)
+          </li>
+          <li>
+            <!-- JAL-3763 -->Spliced transcript CDS sequences don't show
+            CDS features
+          </li>
+          <li>
+            <!-- JAL-3700 -->Selections in CDS sequence panel don't
+            always select corresponding protein sequences
+          </li>
+          <li>
+            <!-- JAL-3759 --> <em>Make groups from selection</em> for a
+            column selection doesn't always ignore hidden columns
+          </li>
+        </ul> <em>Installer</em>
+        <ul>
+          <li>
+            <!-- JAL-3611 -->Space character in Jalview install path on
+            Windows prevents install4j launching getdown
+          </li>
+        </ul> <em>Development</em>
+        <ul>
+          <li>
+            <!-- JAL-3248 -->Fixed typos and specified compatible gradle
+            version numbers in doc/building.md
+          </li>
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" align="center" nowrap><strong><a
+          id="Jalview.2.11.1">2.11.1</a><a id="Jalview.2.11.1.2">.2</a><br />
+          <em>25/09/2020</em></strong></td>
+      <td align="left" valign="top">
+        <ul>
+        </ul>
+      </td>
+      <td align="left" valign="top">
+        <ul>
+          <li>
+            <!-- JAL-3757 -->Fresh install of Jalview 2.11.1.1 reports
+            "Encountered problems opening
+            https://www.jalview.org/examples/exampleFile_2_7.jvp"
+          </li>
+        </ul>
+      </td>
+    </tr>
+    <tr>
+      <td width="60" align="center" nowrap><strong><a
           id="Jalview.2.11.1">2.11.1</a><a id="Jalview.2.11.1.1">.1</a><br />
-          <em>13/07/2020</em></strong></td>
+          <em>17/09/2020</em></strong></td>
       <td align="left" valign="top">
         <ul>
-        <!-- -->
-       </ul>
+          <li>
+            <!-- JAL-3638 -->Shift+arrow keys navigate to next gap or
+            residue in cursor mode
+          </li>
+          <li>
+            <!-- JAL-3695 -->Support import of VCF 4.3 by updating
+            HTSJDK from 2.12 to 2.23
+          </li>
+          <li>
+            <!-- JAL-3621 -->IntervalStore library updated to v.1.1:
+            optimisations and improvements suggested by Bob Hanson and
+            improved compatibility with JalviewJS
+          </li>
+          <li>
+            <!-- JAL-3615 -->Retrieve GZipped stockholm formatted
+            alignments from Pfam and Rfam
+          </li>
+          <li>
+            <!-- JAL-2656 -->Recognise GZipped content for URLs and File
+            import (no longer based on .gz extension)
+          </li>
+          <li>
+            <!-- JAL-3570 -->Updated Spanish Translation for 2.11.1
+          </li>
+          <li>
+            <!-- JAL-3692 -->Migrate EMBL record retrieval to use latest
+            ENA Browser (https://www.ebi.ac.uk/ena/browser/home) and
+            EMBL flat file
+          </li>
+          <li>
+            <!-- JAL-3667 -->Improved warning messages, debug logging
+            and fixed Retry action when Jalview encounters errors when
+            saving or making backup files.
+          </li>
+          <li>
+            <!-- JAL-3676 -->Enhanced Jalview Java Console:
+            <ul>
+              <li>Jalview's logging level can be configured</li>
+              <li>Copy to Clipboard Buttion</li>
+            </ul>
+          </li>
+          <li>
+            <!-- JAL-3541 -->Improved support for Hi-DPI (4K) screens
+            when running on Linux (Requires Java 11+)
+          </li>
+        </ul> <em>Launching Jalview</em>
+        <ul>
+          <li>
+            <!-- JAL-3608 -->Configure Jalview Desktop's look and feel
+            through a system property
+          </li>
+          <li>
+            <!-- JAL-3477 -->Improved built-in documentation and command
+            line help for configuring Jalview's memory
+          </li>                   
+        </ul>
       </td>
       <td align="left" valign="top">
         <ul>
-         <li><!-- JAL-3493 -->Escape does not clear highlights on the alignment (Since Jalview 2.10.3)</li>
-       </ul>
+          <li>
+            <!-- JAL-3691 -->Conservation and Quality tracks are shown
+            but not calculated and no protein or DNA score models are
+            available for tree/PCA calculation when launched with
+            Turkish language locale
+          </li>
+          <li>
+            <!-- JAL-3493 -->Escape does not clear highlights on the
+            alignment (Since Jalview 2.10.3)
+          </li>
+          <li>
+            <!--  JAL-3680 -->Alt+Left or Right arrow in cursor mode
+            doesn't slide selected sequences, just sequence under cursor
+          </li>
+          <li>
+            <!-- JAL-3732 -->Alt+Up/Down in cursor mode doesn't move
+            sequence under the cursor
+          </li>
+          <li>
+            <!-- JAL-3613 -->Peptide-to-CDS tracking broken when
+            multiple EMBL gene products shown for a single contig
+          </li>
+          <li>
+            <!-- JAL-3696 -->Errors encountered when processing variants
+            from VCF files yield "Error processing VCF: Format specifier
+            '%s'" on the console
+          </li>
+          <li>
+            <!-- JAL-3697 -->Count of features not shown can be wrong
+            when there are both local and complementary features mapped
+            to the position under the cursor
+          </li>
+          <li>
+            <!-- JAL-3673 -->Sequence ID for reference sequence is
+            clipped when Right align Sequence IDs enabled
+          </li>
+          <li>
+            <!-- JAL-2983 -->Slider with negative range values not
+            rendered correctly in VAqua4 (Since 2.10.4)
+          </li>
+          <li>
+            <!-- JAL-3685 -->Single quotes not displayed correctly in
+            internationalised text for some messages and log output
+          </li>
+          <li>
+            <!-- JAL-3490 -->Find doesn't report matches that span
+            hidden gapped columns
+          </li>
+          <li>
+            <!-- JAL-3597 -->Resolved memory leaks in Tree and PCA
+            panels, Alignment viewport and annotation renderer.
+          </li>
+          <li>
+            <!-- JAL-3561 -->Jalview ignores file format parameter
+            specifying output format when exporting an alignment via the
+            command line
+          </li>
+          <li>
+            <!-- JAL-3667 -->Windows 10: For a minority of users, if
+            backups are not enabled, Jalview sometimes fails to
+            overwrite an existing file and raises a warning dialog. (in
+            2.11.0, and 2.11.1.0, the workaround is to try to save the
+            file again, and if that fails, delete the original file and
+            save in place.)
+          </li>
+          <li>
+            <!-- JAL-3750 -->Cannot process alignments from HTTPS urls
+            via command line
+          </li>
+          <li>
+            <!-- JAL-3741 -->References to http://www.jalview.org in
+            program and documentation
+          </li>
+        </ul> <em>Launching Jalview</em>
+        <ul>
+          <li>
+            <!-- JAL-3718 -->Jalview application fails when launched the
+            first time for a version that has different jars to the
+            previous launched version.
+          </li>
+        </ul> <em>Developing Jalview</em>
+        <ul>
+          <li>
+            <!-- JAL-3541 -->Fixed issue with cleaning up old coverage
+            data, causing cloverReport gradle task to fail with an
+            OutOfMemory error.
+          </li>
+          <li>
+            <!-- JAL-3280 -->Migrated the Jalview Version Checker to
+            monitor the release channel
+          </li>
+        </ul> <em>New Known defects</em>
+        <ul>
+          <li>
+            <!-- JAL-3748 -->CDS shown in result of submitting proteins
+            in a CDS/Protein alignment to a web service is wrong when
+            proteins share a common transcript sequence (e.g.
+            genome of RNA viruses)
+          </li>
+          <li>
+            <!-- JAL-3576 -->Co-located features exported and re-imported
+            are ordered differently when shown on alignment and in
+            tooltips. (Also affects v2.11.1.0)
+          </li>
+          <li>
+            <!-- JAL-3702 -->Drag and drop of alignment file onto
+            alignment window when in a HiDPI scaled mode in Linux only
+            works for the top left quadrant of the alignment window
+          </li>
+          <li>
+            <!-- JAL-3701 -->Stale build data in jalview standalone jar
+            builds (only affects 2.11.1.1 branch)
+          </li>
+          <li>
+            <!-- JAL-3127 -->Sequence ID colourscheme not re-applied
+            when alignment view restored from project (since Jalview 2.11.0)
+          </li>
+          <li>
+            <!-- JAL-3749 -->Duplicate CDS sequences are generated when
+            protein products for certain ENA records are repeatedly
+            shown via Calculate-&gt;Show Cross Refs
+          </li>
+        </ul>
       </td>
     </tr>
     <tr>
index c3c1d3f..2e8b69a 100644 (file)
@@ -25,7 +25,7 @@
   <p>
     <strong>The Jalview Desktop RSS News Reader</strong><br /> The
     Jalview Desktop includes a built in news reader for the <a
-      href="http://www.jalview.org/feeds/desktop/rss">Jalview
+      href="https://www.jalview.org/feeds/desktop/rss">Jalview
       Desktop News Channel</a>.
   </p>
 
@@ -48,9 +48,9 @@
   <br />
   <p>
     The <em>Jalview news reader</em> was introduced in <a
-      href="http://www.jalview.org/releaseHistory.html#Jalview2.7">Jalview
+      href="https://www.jalview.org/releaseHistory.html#Jalview2.7">Jalview
       version 2.7</a>. Its implementation is based on <a
-      href="http://jswingreader.sourceforge.net/">JSwingReader</a>.
+      href="https://jswingreader.sourceforge.net/">JSwingReader</a>.
   </p>
   <br />
   <em>If you need to prevent the news-reader opening, then add the
index 0f0c7f1..0cf5661 100755 (executable)
 </head>
 <body>
   <p>
-    <strong>Jalview 2.11.1.0</strong>
+    <strong>Welcome to Jalview Version $$Version-Rel$$ !!</strong>
+    <br />Please take a
+    look at the <a href="releases.html#Jalview.$$Version-Rel$$">release
+      notes</a> for this build. Read on for the highlights.
   </p>
   <p>
-    Jalview 2.11.1.0 is the first minor release for the 2.11 series.
-    Along with a number of critical bug fixes and improvements it brings
-    new functionality for mapping sequence features between CDS and
-    Protein alignments. It is also the first release made under a new <em>four</em>
-    number versioning scheme, which will allow us to keep track of
-    patches and bug fixes.
+    <strong>Highlights in 2.11.2</strong>
+  </p>
+  <p><strong>New features for working with 3D Structure</strong><br/>
+    Jalview 2.11.2 features a number of new capabilities:<ul><li><strong>Linked viewing with <em>ChimeraX</em> and <em>PyMol</em></strong><br/>Simply configure your prefered viewer for 3D molecular data in <a href="features/preferences.html#structure">Jalview's structure preferences</a>, make sure that Jalview can locate the viewer's installation, and open a new view via the 3D Structure Chooser!</li>
+      <li><strong>View predicted protein structures via 3D-Beacons</strong><br/>
+       Jalview 2.11.2's <a href="features/structurechooser.html">Structure Chooser includes a client for the 3D-Beacons Network</a>, a new service that allows predicted and observed 3D models for proteins in Uniprot from a range of resources, including AlphaFold DB, SWISS-MODEL and a growing number of other resources. 
+      </li>
+  <p><strong>Retrieval
   </p>
-  <ul>
-    <li><strong>Virtual Features</strong><br />In previous
-      versions of Jalview, specific nucleotide sequence features such as
-      genomic variants and exons were transferred to protein products on
-      import. Jalview 2.11.1 instead provides 'virtual features' that
-      can be enabled and overlaid on linked CDS/Protein views via their
-      <a href="features/splitView.html#virtualfeats">Sequence
-        Features dialog</a>. This allows more analyses of nucleotide and
-      peptide sequence features on alignments in a more flexible and
-      memory efficient way than in earlier versions.<br />
-    <em>Note: Virtual features work best when variants are
-        annotated with CSQ fields. Please <a
-        href="features/importvcf.html#computepepvariants">see this
-          Groovy script workaround</a> if you are working with VCF files
-        without CSQ fields.
-    </em></li>
-    <li><strong>Improved VCF data import</strong><br /> <a
-      href="features/importvcf.html#attribs">Standard attributes for
-        filtering variants</a> (e.g. position, QUAL field etc) are now
-      extracted from VCF files. This new feature was suggested by a user
-      at the Jalview booth during ISMB 2019.</li>
-    <li><strong>Extended feature attributes are exported
-        in GFF3</strong><br />Complex attributes from VCF files can be exported
-      and imported via GFF3</li>
-    <li><strong>Updated Jalview Installer and Launcher</strong><br />Jalview's
-      installation packages are now built with Install4j 8, which brings
-      better support for Linux and improved control of file
-      associations. New <a href="memory.html#jvm">parameters on the
-        Jalview launcher</a> allow an upper memory limit to be specified <em>via</em>
-      a Jalview launch file, to prevent it from hogging your system.</li>
-  </ul>
   <p>
-    See the <a href="releases.html#Jalview.2.11.1.0">2.11.1.0
-      release notes</a> for full details of bugs fixed and new known issues.
+    For the full release notes, see <a
+      href="releases.html#Jalview.2.11.1.4">the Jalview 2.11.1.4
+      release notes</a>.
   </p>
   <p>
-    <em>JalviewJS News</em><br />With the release of Jalview 2.11.1.0,
-    the team are now focused on bringing JalviewJS to full production.
-    To follow our progress take a look at <em>http://www.jalview.org/jalview-js/</em>
-    and follow updates on our new <a
-      href="https://github.com/jalview/jalview-js/">JalviewJS
-      Releases github repository</a>.
+    <strong>Known Issues</strong>
   </p>
+  <p>New known issues in this release affect recovery of CDS/Protein
+    relationships from project files, and interactive selection of
+    protein sequences from a tree built on linked nucleotide sequences.
+    We will provide patches for these issues as soon as possible.</p>
+  </ul>
 </body>
 </html>
diff --git a/j11lib/Jmol-14.31.53.jar b/j11lib/Jmol-14.31.53.jar
new file mode 100644 (file)
index 0000000..5c37cb0
Binary files /dev/null and b/j11lib/Jmol-14.31.53.jar differ
diff --git a/j11lib/Jmol-15.1.3.jar b/j11lib/Jmol-15.1.3.jar
deleted file mode 100644 (file)
index 1672e67..0000000
Binary files a/j11lib/Jmol-15.1.3.jar and /dev/null differ
similarity index 58%
rename from j11lib/groovy-all-2.4.12-indy.jar
rename to j11lib/groovy-all-2.4.21-indy.jar
index bb246a3..15ee98d 100644 (file)
Binary files a/j11lib/groovy-all-2.4.12-indy.jar and b/j11lib/groovy-all-2.4.21-indy.jar differ
diff --git a/j8lib/Jmol-14.29.17.jar b/j8lib/Jmol-14.29.17.jar
deleted file mode 100644 (file)
index 136eb93..0000000
Binary files a/j8lib/Jmol-14.29.17.jar and /dev/null differ
diff --git a/j8lib/Jmol-14.31.53.jar b/j8lib/Jmol-14.31.53.jar
new file mode 100644 (file)
index 0000000..5c37cb0
Binary files /dev/null and b/j8lib/Jmol-14.31.53.jar differ
similarity index 58%
rename from j8lib/groovy-all-2.4.12-indy.jar
rename to j8lib/groovy-all-2.4.21-indy.jar
index bb246a3..15ee98d 100644 (file)
Binary files a/j8lib/groovy-all-2.4.12-indy.jar and b/j8lib/groovy-all-2.4.21-indy.jar differ
diff --git a/lib/Jmol-14.29.17.jar b/lib/Jmol-14.29.17.jar
deleted file mode 100644 (file)
index 136eb93..0000000
Binary files a/lib/Jmol-14.29.17.jar and /dev/null differ
index 278b86e..38f0853 100644 (file)
@@ -75,7 +75,6 @@ Space Group;spacegroup;String;g6;50;400;95;false;false
 Cath Code;cath_code;String;g2;50;400;95;false;false
 Tax Id;tax_id;String;g2;50;400;95;false;false
 Tax Query;tax_query;String;g2;50;400;95;false;false
-Interacting Entity Id;interacting_entity_id;String;g2;50;400;95;false;false
 Interacting Molecules;interacting_molecules;String;g6;50;400;95;false;false
 Pubmed Id;pubmed_id;int;g2;50;400;95;false;false
 Status;status;String;g6;50;400;95;false;false
diff --git a/resources/fts/tdbeacons_data_columns.txt b/resources/fts/tdbeacons_data_columns.txt
new file mode 100644 (file)
index 0000000..4ebcc1b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+tdbeacons_data_columns
+#
+_group.id
+_group.name
+_group.sort_order
+g1;ModelInfo;1
+g2;Quality;2
+g3;Miscellaneous;3
+
+#
+_data_column.primary_key;model_url
+_data_column.default_response_page_size;100
+#
+_data_column.name
+_data_column.code
+_data_column.group_id
+_data_column.data_type  | _data_column.isFormated | _data_column.significantDigit
+_data_column.min_col_width
+_data_column.max_col_width
+_data_column.preferred_col_width
+_data_column.is_shown_by_default
+_data_column.is_searchable
+Uniprot Id;id;String;g1;80;150;85;false;false
+Uniprot Start;uniprot_start;Integer;g1;80;150;85;true;false
+Uniprot End;uniprot_end;Integer;g1;80;150;85;true;false
+Provider;provider;String;g1;80;150;85;true;false
+Model id;model_identifier;String;g3;80;150;85;true;false
+Model Category;model_category;String;g1;80;150;85;true;false
+Model Type;model_type;String;g1;80;150;85;false;false
+Title;model_title;String;g1;100;150;105;true;false
+Resolution;resolution;double|T|3;g2;80;150;85;true;false
+Confidence;confidence_avg_local_score;double|T|2;g2;100;150;105;true;false
+Confidence Score Type;confidence_type;String;g2;100;150;105;true;false
+Confidence Score Version;confidence_version;String;g2;100;150;105;true;false
+Coverage;coverage;double|T|2;g2;80;150;85;true;false
+Sequence Identity;sequence_identity;double|T|1;g2;80;150;85;false;false
+Created Date;created;string;g3;80;150;85;true;false
+UniProt Accession;uniprot_accession;String;g1;50;400;95;false;true
+Url;model_url;String;g3;100;150;105;true;false
+Page URL;model_page_url;String;g3;100;150;105;true;false
+Ensemble Sample Url;ensembl_sample_url;String;g3;100;150;105;false;false
diff --git a/resources/images/3d-beacons-logo-transparent.png b/resources/images/3d-beacons-logo-transparent.png
new file mode 100644 (file)
index 0000000..88aca46
Binary files /dev/null and b/resources/images/3d-beacons-logo-transparent.png differ
index d8217d2..46ebb73 100644 (file)
@@ -118,6 +118,7 @@ action.paste_annotations = Paste Annotations
 action.format = Format
 action.select = Select
 action.new_view = New View
+action.new_structure_view_with = Open new structure view with {0}
 action.close = Close
 action.add = Add
 action.save_as = Save as...
@@ -328,6 +329,7 @@ label.successfully_pasted_alignment_file = Successfully pasted alignment file
 label.paste_your_alignment_file = Paste your alignment file here
 label.paste_your = Paste your
 label.finished_searching = Finished searching
+label.subsequence_matches_found = {0} subsequence matches found
 label.search_results= Search results {0} : {1}
 label.found_match_for = Found match for {0}
 label.font = Font:
@@ -511,6 +513,12 @@ label.load_tree_file = Load a tree file
 label.retrieve_parse_sequence_database_records_alignment_or_selected_sequences = Retrieve and parse sequence database records for the alignment or the currently selected sequences
 label.standard_databases = Standard Databases
 label.fetch_embl_uniprot = Fetch from EMBL/EMBLCDS or Uniprot/PDB and any selected DAS sources
+label.fetch_uniprot_references = Fetch Uniprot references
+label.search_3dbeacons = 3D-Beacons Search
+label.find_models_from_3dbeacons = Search 3D-Beacons for 3D structures and models
+label.3dbeacons = 3D-Beacons
+label.fetch_references_for = Fetch database references for {0} sequences ?
+label.fetch_references_for_3dbeacons = 3D Beacons needs Uniprot References. Fetch database references for {0} sequences ?
 label.reset_min_max_colours_to_defaults = Reset min and max colours to defaults from user preferences.
 label.align_structures_using_linked_alignment_views = Superpose structures using {0} selected alignment view(s)
 label.threshold_feature_display_by_score = Threshold the feature display by score.
@@ -931,7 +939,7 @@ label.groovy_support_failed = Jalview Groovy Support Failed
 label.couldnt_create_groovy_shell = Couldn't create the groovy Shell. Check the error log for the details of what went wrong.
 error.unsupported_version_calcIdparam = Unsupported Version for calcIdparam {0}
 error.implementation_error_cant_reorder_tree = Implementation Error: Can't reorder this tree. Not DefaultMutableTreeNode.
-error.invalid_value_for_option = Invalid value {0} for option {1}
+error.invalid_value_for_option = Invalid value ''{0}'' for option ''{1}''
 error.implementation_error_cannot_import_vamsas_doc = Implementation Error - cannot import existing vamsas document into an existing session, Yet!
 label.vamsas_doc_couldnt_be_opened_as_new_session = VAMSAS Document could not be opened as a new session - please choose another
 error.setstatus_called_non_existent_job_pane = setStatus called for non-existent job pane {0}
@@ -955,7 +963,6 @@ error.implementation_error_minlen_must_be_greater_zero = Implementation error: m
 error.implementation_error_msawbjob_called = Implementation error - StartJob(MsaWSJob) called on a WSJobInstance {0}
 error.implementation_error_cannot_attach_ws_menu_entry = IMPLEMENTATION ERROR: cannot attach WS Menu Entry without service handle reference!
 error.parameter_migration_not_implemented_yet = Parameter migration not implemented yet
-error.implementation_error_cannot_set_jaba_option = Implementation error: cannot set Jaba Option to a value outside its allowed value range!
 error.implementation_error_valuetype_doesnt_support_jabaws_type = IMPLEMENTATION ERROR: jalview.ws.params.ValueConstrainI.ValueType does not support the JABAWS type : {0}
 error.cannot_create_jabaws_param_set = Cannot create a JabaWSParamSet from non-JabaWS parameters
 error.cannot_set_arguments_to_jabaws_param_set = Cannot set arguments to a JabaWSParamSet that are not JabaWS arguments
@@ -1049,7 +1056,6 @@ exception.unable_to_create_internet_config = Unable to create an Internet Config
 exception.invocation_target_calling_url = InvocationTargetException while calling openURL: {0}
 exception.illegal_access_calling_url = IllegalAccessException while calling openURL: {0}
 exception.interrupted_launching_browser = InterruptedException while launching browser: {0}
-exception.ebiembl_retrieval_failed_on = EBI EMBL XML retrieval failed on {0}:{1}
 exception.no_pdb_records_for_chain = No PDB Records for {0} chain {1}
 exception.unexpected_handling_rnaml_translation_for_pdb = Unexpected exception when handling RNAML translation of PDB data
 exception.couldnt_recover_sequence_properties_for_alignment = Couldn't recover sequence properties for alignment
@@ -1101,6 +1107,8 @@ status.collecting_job_results = Collecting job results.
 status.fetching_db_refs = Fetching db refs
 status.loading_cached_pdb_entries = Loading Cached PDB Entries
 status.searching_for_pdb_structures = Searching for PDB Structures
+status.searching_3d_beacons = Searching 3D Beacons
+status.no_structures_discovered_from_3d_beacons = No models discovered from 3D Beacons
 status.opening_file_for = opening file for
 status.colouring_structures = Colouring structures
 label.font_doesnt_have_letters_defined = Font doesn't have letters defined\nso cannot be used\nwith alignment data
@@ -1134,6 +1142,7 @@ label.add_annotations_for = Add annotations for
 action.choose_annotations = Choose Annotations...
 label.choose_annotations = Choose Annotations
 label.find = Find
+label.in = in
 label.invalid_search = Search string invalid
 error.invalid_regex = Invalid regular expression
 label.ignore_gaps_consensus = Ignore Gaps In Consensus
index fdf4201..411643d 100644 (file)
@@ -253,7 +253,7 @@ label.min_value = Valor m
 label.no_value = Sin valor
 label.colour_by_label = Color por etiquetas
 label.new_feature = Nueva función
-label.match_case = Hacer corresponder mayúsculas y minúsculas
+label.match_case = Distinguir min/mayúsculas
 label.view_alignment_editor = Ver en el editor de alineamientos
 label.labels = Etiquetas
 label.output_values = Valores de salida...
@@ -294,6 +294,7 @@ label.successfully_pasted_alignment_file = Fichero de alineamiento pegado exitos
 label.paste_your_alignment_file = Pegar su fichero de alineamiento aquí
 label.paste_your = Pegar su
 label.finished_searching = Búsqueda finalizada
+label.subsequence_matches_found = {0} resultados encontrados en subsequencias
 label.search_results= Buscar Resultados {0} : {1}
 label.found_match_for = Buscar coincidencia para {0}
 label.font = Fuente:
@@ -856,7 +857,7 @@ label.groovy_support_failed = El soporte Groovy de Jalview ha fallado
 label.couldnt_create_groovy_shell = No es posible crear el shell de Groovy. Compruebe el fichero de log para conocer los detalles.
 error.unsupported_version_calcIdparam = Versión no soportada de {0}
 error.implementation_error_cant_reorder_tree = Error de implementación: no es posible reordenar este Ã¡rbol. No DefaultMutableTreeNode.
-error.invalid_value_for_option = Valor no válido de {0} para la opción {1}
+error.invalid_value_for_option = Valor no válido de ''{0}'' para la opción ''{1}''
 error.implementation_error_cannot_import_vamsas_doc = Error de implementación - todavía no es posible importar el documento VAMSAS existente en una sesión existente.
 label.vamsas_doc_couldnt_be_opened_as_new_session = El documento VAMSAS no ha podido abrirse como una nueva sesión. Por favor, escoja otra.
 error.setstatus_called_non_existent_job_pane = se lllamado a setStatus para el panel de trabajo {0} no existente
@@ -880,7 +881,6 @@ error.implementation_error_minlen_must_be_greater_zero = Error de implementaci
 error.implementation_error_msawbjob_called = Error de implementación - StartJob(MsaWSJob) invocado en un WSJobInstance {0}
 error.implementation_error_cannot_attach_ws_menu_entry = Error de implementación: Â¡no es posible adjunto una WS Menu Entry sin una referencia a un manejador del servicio!
 error.parameter_migration_not_implemented_yet = La migración de parámetros no se ha implementado todavía
-error.implementation_error_cannot_set_jaba_option = Error de implementación: no es posible establecer el valor de Jaba Option a un valor fuera de su rango permitido
 error.implementation_error_valuetype_doesnt_support_jabaws_type = Error de implementación: jalview.ws.params.ValueConstrainI.ValueType no soporta el tipo JABAWS: {0}
 error.cannot_create_jabaws_param_set = No es posible crear un JabaWSParamSet con parámetros no JabaWS
 error.cannot_set_arguments_to_jabaws_param_set = No es posible establecer argumentos en JabaWSParamSet que no sean argumentos JabaWS 
@@ -974,7 +974,6 @@ exception.unable_to_create_internet_config = Imposible crear una instancia de co
 exception.invocation_target_calling_url = InvocationTargetException mientras se invocaba openURL: {0}
 exception.illegal_access_calling_url = IllegalAccessException mientras se invocaba openURL: {0}
 exception.interrupted_launching_browser = InterruptedException mientras se lanzaba el navegador: {0}
-exception.ebiembl_retrieval_failed_on = La recuperación de datos EBI EMBL XML ha fallado en {0}:{1}
 exception.no_pdb_records_for_chain = No se han encontrado registros {0} para la cadena {1}
 exception.unexpected_handling_rnaml_translation_for_pdb = Excepcion inesperada cuando se traducían a RNAML los datos PDB
 exception.couldnt_recover_sequence_properties_for_alignment = No es posible recuperar las propiedades de la secuencia para el alineamiento
@@ -1120,6 +1119,7 @@ action.set_as_reference=Marcar como Referencia
 action.unmark_as_reference=Desmarcar como Referencia
 label.open_viewer_failed=Error al abrir {0} - está instalado?\nCompruebe ruta en Preferencias, Estructura
 label.find=Buscar
+label.in = en
 label.select_pdb_file=Seleccionar Fichero PDB
 label.structures_filter=Filtro de Estructuras
 label.scale_protein_to_cdna=Adaptar proteína a cDNA
index 20c19dd..333a4e4 100755 (executable)
                                                                                </xs:documentation>
                                                                        </xs:annotation>
                                                                </xs:attribute>
+                                                               <xs:attribute name="canonical" type="xs:boolean" default="false">
+                                                                       <xs:annotation>
+                                                                               <xs:documentation>
+                                                                                       true for the representative accession for databases where multiple accessions map to the same entry (eg. Uniprot)
+                                                                               </xs:documentation>
+                                                                       </xs:annotation>
+                                                               </xs:attribute>
                                                        </xs:complexType>
                                                </xs:element>
                                        </xs:sequence>
index d7e7937..19d6a8b 100644 (file)
@@ -38,6 +38,9 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -363,7 +366,7 @@ public class ChimeraManager
    * Select something in Chimera
    * 
    * @param command
-   *          the selection command to pass to Chimera
+   *                  the selection command to pass to Chimera
    */
   public void select(String command)
   {
@@ -509,8 +512,8 @@ public class ChimeraManager
 
   /**
    * Return the list of depiction presets available from within Chimera. Chimera
-   * will return the list as a series of lines with the format: Preset type
-   * number "description"
+   * will return the list as a series of lines with the format: Preset type number
+   * "description"
    * 
    * @return list of presets
    */
@@ -551,9 +554,9 @@ public class ChimeraManager
   }
 
   /**
-   * Launch Chimera, unless an instance linked to this object is already
-   * running. Returns true if chimera is successfully launched, or already
-   * running, else false.
+   * Launch Chimera, unless an instance linked to this object is already running.
+   * Returns true if chimera is successfully launched, or already running, else
+   * false.
    * 
    * @param chimeraPaths
    * @return
@@ -696,7 +699,7 @@ public class ChimeraManager
    * Determine the color that Chimera is using for this model.
    * 
    * @param model
-   *          the ChimeraModel we want to get the Color for
+   *                the ChimeraModel we want to get the Color for
    * @return the default model Color for this model in Chimera
    */
   public Color getModelColor(ChimeraModel model)
@@ -713,11 +716,11 @@ public class ChimeraManager
   /**
    * 
    * Get information about the residues associated with a model. This uses the
-   * Chimera listr command. We don't return the resulting residues, but we add
-   * the residues to the model.
+   * Chimera listr command. We don't return the resulting residues, but we add the
+   * residues to the model.
    * 
    * @param model
-   *          the ChimeraModel to get residue information for
+   *                the ChimeraModel to get residue information for
    * 
    */
   public void addResidues(ChimeraModel model)
@@ -809,10 +812,10 @@ public class ChimeraManager
    * Send a command to Chimera.
    * 
    * @param command
-   *          Command string to be send.
+   *                  Command string to be send.
    * @param reply
-   *          Flag indicating whether the method should return the reply from
-   *          Chimera or not.
+   *                  Flag indicating whether the method should return the reply
+   *                  from Chimera or not.
    * @return List of Strings corresponding to the lines in the Chimera reply or
    *         <code>null</code>.
    */
@@ -833,7 +836,7 @@ public class ChimeraManager
      */
     int waited = 0;
     int pause = 25;
-    while (busy  && waited < 1001)
+    while (busy && waited < 1001)
     {
       try
       {
@@ -876,8 +879,15 @@ public class ChimeraManager
     String method = getHttpRequestMethod();
     if ("GET".equals(method))
     {
-      command = command.replace(" ", "+").replace("#", "%23")
-              .replace("|", "%7C").replace(";", "%3B");
+      try
+      {
+        command = URLEncoder.encode(command, StandardCharsets.UTF_8.name());
+      } catch (UnsupportedEncodingException e)
+      {
+        command = command.replace(" ", "+").replace("#", "%23")
+                .replace("|", "%7C").replace(";", "%3B")
+                .replace(":", "%3A");
+      }
     }
     commands.add(new BasicNameValuePair("command", command));
 
index 5cf8a73..0a248cf 100644 (file)
@@ -69,13 +69,13 @@ public class StructureManager
    * 0.93 is ChimeraX latest, 1.0 expected soon
    */
   private static String[] CHIMERA_VERSIONS = new String[] { "1.16.2",
-      "1.16.1", "1.16",
-      "1.15.2", "1.15.1", "1.15", "1.14.2", "1.14.1", "1.14",
-      "1.13.1", "1.13", "1.12.2", "1.12.1", "1.12", "1.11.2",
+      "1.16.1", "1.16", "1.15.2", "1.15.1", "1.15", "1.14.2", "1.14.1",
+      "1.14", "1.13.1", "1.13", "1.12.2", "1.12.1", "1.12", "1.11.2",
       "1.11.2", "1.11.1", "1.11" };
 
-  private static String[] CHIMERAX_VERSIONS = new String[] { "1.0", "0.93",
-      "0.92", "0.91", "0.9" };
+  // Missing 1.1 as this has known bug see JAL-2422
+  private static String[] CHIMERAX_VERSIONS = new String[] { "1.2.5", "1.0",
+      "0.93", "0.92", "0.91", "0.9" };
 
   static final String[] defaultStructureKeys = { "Structure", "pdb",
       "pdbFileName", "PDB ID", "structure", "biopax.xref.PDB", "pdb_ids",
@@ -561,8 +561,8 @@ public class StructureManager
 
   /**
    * This is called by the selectionListener to let us know that the user has
-   * changed their selection in Chimera. We need to go back to Chimera to find
-   * out what is currently selected and update our list.
+   * changed their selection in Chimera. We need to go back to Chimera to find out
+   * what is currently selected and update our list.
    */
   public void chimeraSelectionChanged()
   {
@@ -677,11 +677,11 @@ public class StructureManager
   }
 
   /**
-   * Add a selection to the selection list. This is called primarily by the
-   * Model Navigator Dialog to keep the selections in sync
+   * Add a selection to the selection list. This is called primarily by the Model
+   * Navigator Dialog to keep the selections in sync
    * 
    * @param selectionToAdd
-   *          the selection to add to our list
+   *                         the selection to add to our list
    */
   public void addChimSelection(ChimeraStructuralObject selectionToAdd)
   {
@@ -698,7 +698,7 @@ public class StructureManager
    * Model Navigator Dialog to keep the selections in sync
    * 
    * @param selectionToRemove
-   *          the selection to remove from our list
+   *                            the selection to remove from our list
    */
   public void removeChimSelection(ChimeraStructuralObject selectionToRemove)
   {
@@ -936,16 +936,10 @@ public class StructureManager
     // }
     // }
 
-    /*
-     * Jalview addition: check if path set in user preferences
-     */
+    String os = System.getProperty("os.name");
     String userPath = Cache
             .getDefault(isChimeraX ? Preferences.CHIMERAX_PATH
                     : Preferences.CHIMERA_PATH, null);
-    if (userPath != null)
-    {
-      pathList.add(userPath);
-    }
 
     /*
      * paths are based on getChimeraPaths() in
@@ -957,15 +951,61 @@ public class StructureManager
     String chimera = isChimeraX ? "ChimeraX" : "Chimera";
     String chimeraExe = isChimeraX ? "ChimeraX" : "chimera";
 
+    /*
+     * Jalview addition: check if path set in user preferences
+     */
+    if (userPath != null)
+    {
+      // in macos, deal with the user selecting the .app folder
+      boolean adjusted = false;
+      if (os.startsWith("Mac") && userPath.endsWith((".app")))
+      {
+        String possiblePath = String.format("%s/Contents/MacOS/%s",
+                userPath, chimeraExe);
+        if (new File(possiblePath).exists())
+        {
+          pathList.add(possiblePath);
+          adjusted = true;
+        }
+      }
+      if (!adjusted)
+      {
+        pathList.add(userPath);
+      }
+    }
+
     // Add default installation paths
-    String os = System.getProperty("os.name");
     if (os.startsWith("Linux"))
     {
-      // todo should this be /chimeraX/ for ChimeraX? not in structureVizX code
-      pathList.add("/usr/local/chimera/bin/" + chimeraExe);
-      pathList.add("/usr/local/bin/" + chimeraExe);
-      pathList.add("/usr/bin/" + chimeraExe);
-      pathList.add(System.getProperty("user.home") + "/opt/bin/" + chimeraExe);
+      // ChimeraX .deb and .rpm packages put symbolic link from /usr/bin/chimerax
+      pathList.add(String.format("/usr/bin/%s", chimeraExe.toLowerCase()));
+      pathList.add(String.format("/usr/bin/%s", chimeraExe));
+
+      pathList.add(
+              String.format("/usr/local/bin/%s", chimeraExe.toLowerCase()));
+      pathList.add(String.format("/usr/local/bin/%s", chimeraExe));
+
+      // these paths also used by .deb and .rpm
+      pathList.add(String.format("/usr/lib/ucsf-%s/bin/%s",
+              chimera.toLowerCase(), chimeraExe));
+      pathList.add(String.format("/usr/libexec/UCSF-%s/bin/%s", chimera,
+              chimeraExe));
+
+      pathList.add(String.format("/usr/local/chimera/bin/%s", chimeraExe));
+
+      // user home paths
+      pathList.add(String.format("%s/bin/%s",
+              System.getProperty("user.home"), chimeraExe.toLowerCase()));
+      pathList.add(String.format("%s/bin/%s",
+              System.getProperty("user.home"), chimeraExe));
+      pathList.add(String.format("%s/opt/bin/%s",
+              System.getProperty("user.home"), chimeraExe.toLowerCase()));
+      pathList.add(String.format("%s/opt/bin/%s",
+              System.getProperty("user.home"), chimeraExe));
+      pathList.add(String.format("%s/local/bin/%s",
+              System.getProperty("user.home"), chimeraExe.toLowerCase()));
+      pathList.add(String.format("%s/local/bin/%s",
+              System.getProperty("user.home"), chimeraExe));
     }
     else if (os.startsWith("Windows"))
     {
@@ -983,12 +1023,32 @@ public class StructureManager
           pathList.add(path);
           pathList.add(path + ".exe");
         }
+        // try without a version number too
+        String path = String.format("%s\\%s\\bin\\%s", root, chimera,
+                chimeraExe);
+        pathList.add(path);
+        pathList.add(path + ".exe");
       }
     }
     else if (os.startsWith("Mac"))
     {
+      // check for installations with version numbers first
+      String[] candidates = isChimeraX ? CHIMERAX_VERSIONS
+              : CHIMERA_VERSIONS;
+      for (String version : candidates)
+      {
+        pathList.add(
+                String.format("/Applications/%s-%s.app/Contents/MacOS/%s",
+                        chimera, version, chimeraExe));
+        pathList.add(
+                String.format("%s/Applications/%s-%s.app/Contents/MacOS/%s",
+                        System.getProperty("user.home"), chimera, version,
+                        chimeraExe));
+      }
       pathList.add(String.format("/Applications/%s.app/Contents/MacOS/%s",
               chimera, chimeraExe));
+      pathList.add(String.format("%s/Applications/%s.app/Contents/MacOS/%s",
+              System.getProperty("user.home"), chimera, chimeraExe));
     }
     return pathList;
   }
index 1b2578e..2fb6ce1 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import java.util.Locale;
+
 import jalview.analysis.scoremodels.PIDModel;
 import jalview.analysis.scoremodels.ScoreMatrix;
 import jalview.analysis.scoremodels.ScoreModels;
@@ -148,7 +150,7 @@ public class AlignSeq
   public AlignSeq(SequenceI s1, String string1, SequenceI s2,
           String string2, String type)
   {
-    seqInit(s1, string1.toUpperCase(), s2, string2.toUpperCase(), type);
+    seqInit(s1, string1.toUpperCase(Locale.ROOT), s2, string2.toUpperCase(Locale.ROOT), type);
   }
 
   /**
index 0c40873..23c5d64 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import java.util.Locale;
+
 import jalview.commands.RemoveGapColCommand;
 import jalview.datamodel.AlignedCodon;
 import jalview.datamodel.AlignedCodonFrame;
@@ -181,9 +183,9 @@ public class AlignmentUtils
       // TODO use Character.toLowerCase to avoid creating String objects?
       char[] upstream = new String(ds
               .getSequence(s.getStart() - 1 - ustream_ds, s.getStart() - 1))
-                      .toLowerCase().toCharArray();
+                      .toLowerCase(Locale.ROOT).toCharArray();
       char[] downstream = new String(
-              ds.getSequence(s_end - 1, s_end + dstream_ds)).toLowerCase()
+              ds.getSequence(s_end - 1, s_end + dstream_ds)).toLowerCase(Locale.ROOT)
                       .toCharArray();
       char[] coreseq = s.getSequence();
       char[] nseq = new char[offset + upstream.length + downstream.length
@@ -462,7 +464,7 @@ public class AlignmentUtils
     if (cdnaLength != mappedLength && cdnaLength > 2)
     {
       String lastCodon = String.valueOf(cdnaSeqChars,
-              cdnaLength - CODON_LENGTH, CODON_LENGTH).toUpperCase();
+              cdnaLength - CODON_LENGTH, CODON_LENGTH).toUpperCase(Locale.ROOT);
       for (String stop : ResidueProperties.STOP_CODONS)
       {
         if (lastCodon.equals(stop))
@@ -479,7 +481,7 @@ public class AlignmentUtils
      */
     int startOffset = 0;
     if (cdnaLength != mappedLength && cdnaLength > 2
-            && String.valueOf(cdnaSeqChars, 0, CODON_LENGTH).toUpperCase()
+            && String.valueOf(cdnaSeqChars, 0, CODON_LENGTH).toUpperCase(Locale.ROOT)
                     .equals(ResidueProperties.START))
     {
       startOffset += CODON_LENGTH;
index 2f556f1..c9ed570 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import java.util.Locale;
+
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
@@ -368,7 +370,7 @@ public class AnnotationSorter
     {
       return 1;
     }
-    return label1.toUpperCase().compareTo(label2.toUpperCase());
+    return label1.toUpperCase(Locale.ROOT).compareTo(label2.toUpperCase(Locale.ROOT));
   }
 
   /**
index ff38c08..6cc9dd3 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import java.util.Locale;
+
 import jalview.analysis.scoremodels.ScoreMatrix;
 import jalview.analysis.scoremodels.ScoreModels;
 import jalview.datamodel.AlignmentAnnotation;
@@ -312,7 +314,7 @@ public class Conservation
   protected static void recordConservation(Map<String, Integer> resultMap,
           String res)
   {
-    res = res.toUpperCase();
+    res = res.toUpperCase(Locale.ROOT);
     for (Entry<String, Map<String, Integer>> property : ResidueProperties.propHash
             .entrySet())
     {
index d52e42a..bf86a86 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import java.util.Locale;
+
 import jalview.api.AlignViewportI;
 import jalview.api.FinderI;
 import jalview.datamodel.AlignmentI;
@@ -148,7 +150,7 @@ public class Finder implements FinderI
     idMatches = new ArrayList<>();
 
     String searchString = matchCase ? theSearchString
-            : theSearchString.toUpperCase();
+            : theSearchString.toUpperCase(Locale.ROOT);
     Regex searchPattern = new Regex(searchString);
     searchPattern.setIgnoreCase(!matchCase);
 
index df1dd82..8d69b0b 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import java.util.Locale;
+
 import jalview.bin.Cache;
 
 import java.io.BufferedReader;
@@ -198,13 +200,13 @@ public final class GeneticCodes
       while (line != null)
       {
         line = readLine(dataIn);
-        if (line != null && !"DNA".equals(line.toUpperCase()))
+        if (line != null && !"DNA".equals(line.toUpperCase(Locale.ROOT)))
         {
           String[] tokens = line.split("\\t");
           if (tokens.length == 2)
           {
-          ambiguityCodes.put(tokens[0].toUpperCase(),
-                  tokens[1].toUpperCase());
+          ambiguityCodes.put(tokens[0].toUpperCase(Locale.ROOT),
+                  tokens[1].toUpperCase(Locale.ROOT));
           }
           else
           {
@@ -341,13 +343,13 @@ public final class GeneticCodes
       @Override
       public String translateCanonical(String codon)
       {
-        return codons.get(codon.toUpperCase());
+        return codons.get(codon.toUpperCase(Locale.ROOT));
       }
 
       @Override
       public String translate(String codon)
       {
-        String upper = codon.toUpperCase();
+        String upper = codon.toUpperCase(Locale.ROOT);
         String peptide = translateCanonical(upper);
 
         /*
index a85c7f3..3ec162d 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import java.util.Locale;
+
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.SequenceI;
 
@@ -289,7 +291,7 @@ public class SequenceIdMatcher
     {
       if (s != null)
       {
-        id = s.toLowerCase();
+        id = s.toLowerCase(Locale.ROOT);
       }
       else
       {
@@ -319,7 +321,7 @@ public class SequenceIdMatcher
       {
         if (s instanceof String)
         {
-          return this.stringequals(((String) s).toLowerCase());
+          return this.stringequals(((String) s).toLowerCase(Locale.ROOT));
         }
       }
 
index 672d6b4..ed6358e 100644 (file)
@@ -112,4 +112,6 @@ public interface DBRefEntryI
    *         associated sequence object
    */
   public boolean isPrimaryCandidate();
+
+  public boolean isCanonical();
 }
index 8aa2858..fff3b38 100644 (file)
@@ -297,4 +297,11 @@ public interface FeatureRenderer
    * @return
    */
   MappedFeatures findComplementFeaturesAtResidue(SequenceI sequence, int pos);
+
+  /**
+   * Sends a message to let any registered parties know that something about
+   * feature rendering has changed
+   */
+  void notifyFeaturesChanged();
+
 }
index c0fc523..c8a835a 100644 (file)
@@ -35,7 +35,8 @@ public interface FeatureSettingsModelI extends Comparator<String>
   // interface, simplifying instantiating classes
 
   /**
-   * Answers true if the specified feature type is displayed
+   * Answers true if the specified feature type is to be displayed, false if no
+   * preference
    * 
    * @param type
    * @return
@@ -43,6 +44,15 @@ public interface FeatureSettingsModelI extends Comparator<String>
   boolean isFeatureDisplayed(String type);
 
   /**
+   * Answers true if the specified feature type is to be hidden, false if no
+   * preference
+   * 
+   * @param type
+   * @return
+   */
+  boolean isFeatureHidden(String type);
+
+  /**
    * Answers true if the specified feature group is displayed
    * 
    * @param group
index e69785f..9387e3f 100644 (file)
@@ -39,6 +39,8 @@ public interface FeaturesDisplayedI
 
   void setVisible(String featureType);
 
+  void setHidden(String featureType);
+
   /**
    * Sets all the specified feature types to visible. Visibility of other
    * feature types is not changed.
index 4a83b35..29f0d84 100755 (executable)
@@ -182,8 +182,8 @@ import jalview.ws.sifts.SiftsSettings;
  * when shading by annotation</li>
  * <li>ANNOTATIONCOLOUR_MAX (red) Shade used for maximum value of annotation
  * when shading by annotation</li>
- * <li>www.jalview.org (http://www.jalview.org) a property enabling all HTTP
- * requests to be redirected to a mirror of http://www.jalview.org</li>
+ * <li>www.jalview.org (https://www.jalview.org) a property enabling all HTTP
+ * requests to be redirected to a mirror of https://www.jalview.org</li>
  * <li>FIGURE_AUTOIDWIDTH (false) Expand the left hand column of an exported
  * alignment figure to accommodate even the longest sequence ID or annotation
  * label.</li>
@@ -370,16 +370,24 @@ public class Cache
   }
 
   /**
-   * Loads properties from the given properties file. Any existing properties
-   * are first cleared.
+   * Loads properties from the given properties file. Any existing properties are
+   * first cleared.
    */
   public static void loadProperties(String propsFile)
   {
     propertiesFile = propsFile;
+    String releasePropertiesFile = null;
+    boolean defaultProperties = false;
     if (propsFile == null && !propsAreReadOnly)
     {
+      String channelPrefsFilename = ChannelProperties
+              .getProperty("preferences.filename");
+      String releasePrefsFilename = ".jalview_properties";
       propertiesFile = System.getProperty("user.home") + File.separatorChar
-              + ".jalview_properties";
+              + channelPrefsFilename;
+      releasePropertiesFile = System.getProperty("user.home")
+              + File.separatorChar + releasePrefsFilename;
+      defaultProperties = true;
     }
     else
     {
@@ -399,20 +407,29 @@ public class Cache
         InputStream fis;
         try
         {
+          // props file provided as URL
           fis = new URL(propertiesFile).openStream();
           System.out.println(
                   "Loading jalview properties from : " + propertiesFile);
           System.out.println(
                   "Disabling Jalview writing to user's local properties file.");
           propsAreReadOnly = true;
-
         } catch (Exception ex)
         {
           fis = null;
         }
         if (fis == null)
         {
-          fis = new FileInputStream(propertiesFile);
+          String readPropertiesFile = propertiesFile;
+          // if we're using the usual properties file and the channel properties
+          // file doesn't exist, read .jalview_properties
+          // (but we'll still save to the channel properties file).
+          if (defaultProperties && (!new File(propertiesFile).exists())
+                  && (new File(releasePropertiesFile).exists()))
+          {
+            readPropertiesFile = releasePropertiesFile;
+          }
+          fis = new FileInputStream(readPropertiesFile);
         }
         applicationProperties.clear();
         applicationProperties.load(fis);
@@ -506,7 +523,7 @@ public class Cache
                     DEFAULT_CACHE_THRESHOLD_IN_DAYS));
 
     IdOrgSettings.setUrl(getDefault("ID_ORG_HOSTURL",
-            "http://www.jalview.org/services/identifiers"));
+            "https://www.jalview.org/services/identifiers"));
     IdOrgSettings.setDownloadLocation(ID_ORG_FILE);
 
     StructureImportSettings.setDefaultStructureFileFormat(jalview.bin.Cache
@@ -693,11 +710,10 @@ public class Cache
   }
 
   /**
-   * Gets Jalview application property of given key. Returns null if key not
-   * found
+   * Gets Jalview application property of given key. Returns null if key not found
    * 
    * @param key
-   *          Name of property
+   *              Name of property
    * 
    * @return Property value
    */
@@ -713,8 +729,8 @@ public class Cache
   }
 
   /**
-   * These methods are used when checking if the saved preference is different
-   * to the default setting
+   * These methods are used when checking if the saved preference is different to
+   * the default setting
    */
 
   public static boolean getDefault(String property, boolean def)
@@ -747,8 +763,8 @@ public class Cache
   }
 
   /**
-   * Answers the value of the given property, or the supplied default value if
-   * the property is not set
+   * Answers the value of the given property, or the supplied default value if the
+   * property is not set
    */
   public static String getDefault(String property, String def)
   {
@@ -760,9 +776,9 @@ public class Cache
    * Stores property in the file "HOME_DIR/.jalview_properties"
    * 
    * @param key
-   *          Name of object
+   *              Name of object
    * @param obj
-   *          String value of property
+   *              String value of property
    * 
    * @return previous value of property (or null)
    */
@@ -1150,7 +1166,7 @@ public class Cache
    * Loads in user colour schemes from files.
    * 
    * @param files
-   *          a '|'-delimited list of file paths
+   *                a '|'-delimited list of file paths
    */
   public static void initUserColourSchemes(String files)
   {
@@ -1459,8 +1475,8 @@ public class Cache
                   // open Preferences -> Connections
                   String message = MessageManager
                           .getString("label.proxy_password_required");
-                  Preferences.openPreferences(Preferences.CONNECTIONS_TAB,
-                          message);
+                  Preferences.openPreferences(
+                          Preferences.TabRef.CONNECTIONS_TAB, message);
                   Preferences.getInstance()
                           .proxyAuthPasswordCheckHighlight(true, true);
                 }
index d6d440a..6219179 100644 (file)
@@ -1,5 +1,7 @@
 package jalview.bin;
 
+import java.util.Locale;
+
 import java.awt.HeadlessException;
 
 public class HiDPISetting
@@ -34,6 +36,8 @@ public class HiDPISetting
 
   public static int scale = 0;
 
+  public final static int MAX_SCALE = 8;
+
   private static boolean doneInit = false;
 
   private static boolean allowScalePropertyArg = false;
@@ -43,7 +47,7 @@ public class HiDPISetting
   static
   {
     String system = System.getProperty("os.name") == null ? null
-            : System.getProperty("os.name").toLowerCase();
+            : System.getProperty("os.name").toLowerCase(Locale.ROOT);
     if (system != null)
     {
       isLinux = system.indexOf("linux") > -1;
@@ -66,8 +70,17 @@ public class HiDPISetting
 
     // get and use command line property values first
     String setHiDPIProperty = System.getProperty(setHiDPIPropertyName);
-    setHiDPI = setHiDPIProperty != null
-            && setHiDPIProperty.equalsIgnoreCase("true");
+    boolean setHiDPIPropertyBool = Boolean.parseBoolean(setHiDPIProperty);
+
+    // allow -DsetHiDPI=false to turn off HiDPI scaling
+    if (setHiDPIProperty != null && !setHiDPIPropertyBool)
+    {
+      clear();
+      doneInit = true;
+      return;
+    }
+
+    setHiDPI = setHiDPIProperty != null && setHiDPIPropertyBool;
 
     String setHiDPIScaleProperty = System
             .getProperty(setHiDPIScalePropertyName);
@@ -76,6 +89,12 @@ public class HiDPISetting
       try
       {
         setHiDPIScale = Integer.parseInt(setHiDPIScaleProperty);
+        // if setHiDPIScale property is validly set and setHiDPI property wasn't
+        // attempted to be set we assume setHiDPIScale to be true
+        if (setHiDPIProperty == null)
+        {
+          setHiDPI = true;
+        }
       } catch (NumberFormatException e)
       {
         System.err.println(setHiDPIScalePropertyName + " property give ("
@@ -149,6 +168,14 @@ public class HiDPISetting
 
     int dimensionScale = 1 + (mindimension / bigScreenThreshold);
 
+    // reject outrageous values -- dpiScale in particular could be mistaken
+    if (dpiScale > MAX_SCALE) {
+      dpiScale = 1;
+    }
+    if (dimensionScale > MAX_SCALE) {
+      dimensionScale = 1;
+    }
+
     // choose larger of dimensionScale or dpiScale (most likely dimensionScale
     // as dpiScale often misreported)
     int autoScale = Math.max(dpiScale, dimensionScale);
index a1a6846..73227d3 100755 (executable)
@@ -38,6 +38,7 @@ import java.security.PermissionCollection;
 import java.security.Permissions;
 import java.security.Policy;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Vector;
 import java.util.logging.ConsoleHandler;
@@ -454,7 +455,7 @@ public class Jalview
               // String defurl =
               // "http://anaplog.compbio.dundee.ac.uk/cgi-bin/questionnaire.pl";
               // //
-              String defurl = "http://www.jalview.org/cgi-bin/questionnaire.pl";
+              String defurl = "https://www.jalview.org/cgi-bin/questionnaire.pl";
               Cache.log.debug(
                       "Starting questionnaire with default url: " + defurl);
               desktop.checkForQuestionnaire(defurl);
@@ -783,14 +784,18 @@ public class Jalview
      * @j2sIgnore
      */
     {
-      file = Cache.getDefault("STARTUP_FILE",
-              Cache.getDefault("www.jalview.org", "http://www.jalview.org")
-                      + "/examples/exampleFile_2_7.jar");
+      file = jalview.bin.Cache.getDefault("STARTUP_FILE",
+              jalview.bin.Cache.getDefault("www.jalview.org",
+                      "https://www.jalview.org")
+                      + "/examples/exampleFile_2_7.jvp");
       if (file.equals(
-              "http://www.jalview.org/examples/exampleFile_2_3.jar"))
+              "http://www.jalview.org/examples/exampleFile_2_3.jar") || file.equals(
+                      "http://www.jalview.org/examples/exampleFile_2_7.jar"))
       {
+        file.replace("http:", "https:");
         // hardwire upgrade of the startup file
-        file.replace("_2_3.jar", "_2_7.jar");
+        file.replace("_2_3", "_2_7");
+        file.replace("2_7.jar", "2_7.jvp");
         // and remove the stale setting
         Cache.removeProperty("STARTUP_FILE");
       }
@@ -987,9 +992,9 @@ public class Jalview
       for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
       {
         if (info.getName() != null && nameStartsWith
-                ? info.getName().toLowerCase()
-                        .startsWith(name.toLowerCase())
-                : info.getName().equalsIgnoreCase(name.toLowerCase()))
+                ? info.getName().toLowerCase(Locale.ROOT)
+                        .startsWith(name.toLowerCase(Locale.ROOT))
+                : info.getName().toLowerCase(Locale.ROOT).equals(name.toLowerCase(Locale.ROOT)))
         {
           className = info.getClassName();
           break;
@@ -1059,7 +1064,7 @@ public class Jalview
     System.setProperty("apple.laf.useScreenMenuBar", "true");
     set = setQuaquaLookAndFeel();
     if ((!set) || !UIManager.getLookAndFeel().getClass().toString()
-            .toLowerCase().contains("quaqua"))
+            .toLowerCase(Locale.ROOT).contains("quaqua"))
     {
       set = setVaquaLookAndFeel();
     }
@@ -1106,7 +1111,7 @@ public class Jalview
                     + "-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"
                     + "-jvmmempc=PERCENT\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to PERCENT% of total physical memory detected. This defaults to 90 if total physical memory can be detected. See https://www.jalview.org/help/html/memory.html for more details.\n"
                     + "-jvmmemmax=MAXMEMORY\tOnly available with standalone executable jar or jalview.bin.Launcher. Limit maximum heap size (memory) to MAXMEMORY. MAXMEMORY can be specified in bytes, kilobytes(k), megabytes(m), gigabytes(g) or if you're lucky enough, terabytes(t). This defaults to 32g if total physical memory can be detected, or to 8g if total physical memory cannot be detected. See https://www.jalview.org/help/html/memory.html for more details.\n"
-                    + "\n~Read documentation in Application or visit http://www.jalview.org for description of Features and Annotations file~\n\n");
+                    + "\n~Read documentation in Application or visit https://www.jalview.org for description of Features and Annotations file~\n\n");
   }
 
   private static void startUsageStats(final Desktop desktop)
index e7f2a53..871ca54 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.bin;
 
+import java.util.Locale;
+
 import jalview.analysis.AlignmentUtils;
 import jalview.api.StructureSelectionManagerProvider;
 import jalview.appletgui.AlignFrame;
@@ -208,7 +210,7 @@ public class JalviewLite extends Applet
       final int pos = apos;
       // use vamsas listener to broadcast to all listeners in scope
       if (alignedPosition != null && (alignedPosition.trim().length() == 0
-              || alignedPosition.toLowerCase().indexOf("false") > -1))
+              || alignedPosition.toLowerCase(Locale.ROOT).indexOf("false") > -1))
       {
         java.awt.EventQueue.invokeLater(new Runnable()
         {
@@ -412,7 +414,7 @@ public class JalviewLite extends Applet
             r--;
           } catch (NumberFormatException ex)
           {
-            if (cl.toLowerCase().equals("sequence"))
+            if (cl.toLowerCase(Locale.ROOT).equals("sequence"))
             {
               // we are in the dataset sequence's coordinate frame.
               inseqpos = true;
@@ -1440,7 +1442,7 @@ public class JalviewLite extends Applet
     String externalsviewer = getParameter("externalstructureviewer");
     if (externalsviewer != null)
     {
-      useXtrnalSviewer = externalsviewer.trim().toLowerCase().equals(TRUE);
+      useXtrnalSviewer = externalsviewer.trim().toLowerCase(Locale.ROOT).equals(TRUE);
     }
     /**
      * if true disable the check for jmol
@@ -2691,7 +2693,7 @@ public class JalviewLite extends Applet
           final String groups, boolean state)
   {
     final boolean st = state;// !(state==null || state.equals("") ||
-    // state.toLowerCase().equals("false"));
+    // state.toLowerCase(Locale.ROOT).equals("false"));
     java.awt.EventQueue.invokeLater(new Runnable()
     {
       @Override
index b8d31c2..e13f2dd 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.bin;
 
+import java.util.Locale;
+
 import java.io.File;
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
@@ -190,7 +192,7 @@ public class Launcher
       process.waitFor();
     } catch (IOException e)
     {
-      if (e.getMessage().toLowerCase().contains("memory"))
+      if (e.getMessage().toLowerCase(Locale.ROOT).contains("memory"))
       {
         System.out.println("Caught a memory exception: " + e.getMessage());
         // Probably the "Cannot allocate memory" error, try without the memory
index 5d7f14c..52f0c9e 100644 (file)
@@ -31,6 +31,8 @@ package jalview.bin;
  * @author bsoares
  *
  */
+import java.util.Locale;
+
 public class MemorySetting
 {
   public static final String MAX_HEAPSIZE_PERCENT_PROPERTY_NAME = "jvmmempc";
@@ -101,7 +103,7 @@ public class MemorySetting
     if (jvmmemmax != null && jvmmemmax.length() > 0)
     {
       long multiplier = 1;
-      switch (jvmmemmax.toLowerCase().substring(jvmmemmax.length() - 1))
+      switch (jvmmemmax.toLowerCase(Locale.ROOT).substring(jvmmemmax.length() - 1))
       {
       case "t":
         multiplier = 1099511627776L; // 2^40
index 7f7142f..2d61705 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.commands;
 
+import java.util.Locale;
+
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceI;
 
@@ -101,13 +103,13 @@ public class ChangeCaseCommand implements CommandI
         if ((caseChange == TO_UPPER && doCommand)
                 || (caseChange == TO_LOWER && !doCommand))
         {
-          newSeq.append(sequence.substring(start, end).toUpperCase());
+          newSeq.append(sequence.substring(start, end).toUpperCase(Locale.ROOT));
         }
 
         else if ((caseChange == TO_LOWER && doCommand)
                 || (caseChange == TO_UPPER && !doCommand))
         {
-          newSeq.append(sequence.substring(start, end).toLowerCase());
+          newSeq.append(sequence.substring(start, end).toLowerCase(Locale.ROOT));
         }
 
         else
index d0790c8..30595bc 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.commands;
 
+import java.util.Locale;
+
 import jalview.analysis.AlignSeq;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
@@ -789,7 +791,7 @@ public class EditCommand implements CommandI
       String nogapold = AlignSeq.extractGaps(Comparison.GapChars,
               new String(command.string[i]));
 
-      if (!nogaprep.toLowerCase().equals(nogapold.toLowerCase()))
+      if (!nogaprep.toLowerCase(Locale.ROOT).equals(nogapold.toLowerCase(Locale.ROOT)))
       {
         // we may already have dataset and limits stashed...
         if (newDSWasNeeded || newStartEndWasNeeded)
@@ -838,7 +840,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.toUpperCase());
+            newds.setSequence(fullseq.toUpperCase(Locale.ROOT));
 
             if (command.oldds == null)
             {
@@ -891,7 +893,7 @@ public class EditCommand implements CommandI
               // new
               // start/end
               String nogapalseq = AlignSeq.extractGaps(Comparison.GapChars,
-                      command.seqs[i].getSequenceAsString().toUpperCase());
+                      command.seqs[i].getSequenceAsString().toUpperCase(Locale.ROOT));
               int newStart = command.seqs[i].getDatasetSequence()
                       .getSequenceAsString().indexOf(nogapalseq);
               if (newStart == -1)
index 2ee4503..d41cdd4 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.datamodel;
 
+import java.util.Locale;
+
 import jalview.analysis.Rna;
 import jalview.analysis.SecStrConsensus.SimpleBP;
 import jalview.analysis.WUSSParseException;
@@ -1225,7 +1227,7 @@ public class AlignmentAnnotation
   {
     if (seqname && this.sequenceRef != null)
     {
-      int i = description.toLowerCase().indexOf("<html>");
+      int i = description.toLowerCase(Locale.ROOT).indexOf("<html>");
       if (i > -1)
       {
         // move the html tag to before the sequence reference.
index f557ff8..4a5c888 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.datamodel;
 
+import java.util.Locale;
+
 import jalview.api.DBRefEntryI;
 import jalview.util.DBRefUtils;
 import jalview.util.MapList;
@@ -39,6 +41,8 @@ public class DBRefEntry implements DBRefEntryI
   int sourceKey = Integer.MIN_VALUE;
 
   String canonicalSourceName;
+  
+  boolean isCanonicalAccession;
 
   /*
    * maps from associated sequence to the database sequence's coordinate system
@@ -61,12 +65,25 @@ public class DBRefEntry implements DBRefEntryI
    */
   public DBRefEntry(String source, String version, String accessionId)
   {
-    this(source, version, accessionId, null);
+    this(source, version, accessionId, null,false);
   }
 
   /**
    * 
    * @param source
+   *                      may not be null
+   * @param version
+   *                      may be null
+   * @param accessionId
+   *                      may be null
+   */
+  public DBRefEntry(String source, String version, String accessionId, Mapping map)
+  {
+    this(source, version, accessionId, map,false);
+  }
+  /**
+   * 
+   * @param source
    *          canonical source (turned to uppercase; cannot be null)
    * @param version
    *          (source dependent version string or null)
@@ -77,13 +94,14 @@ public class DBRefEntry implements DBRefEntryI
    *          numbering or null)
    */
   public DBRefEntry(String source, String version, String accessionId,
-          Mapping map)
+          Mapping map,boolean isCanonical)
   {
        
-    this.source = source.toUpperCase();
+    this.source = source.toUpperCase(Locale.ROOT);
     setVersion(version);
     this.accessionId = accessionId;
     this.map = map;
+    this.isCanonicalAccession=isCanonical;
   }
 
   /**
@@ -97,7 +115,7 @@ public class DBRefEntry implements DBRefEntryI
                     : new String(entry.getVersion())),
             (entry.getAccessionId() == null ? ""
                     : new String(entry.getAccessionId())),
-            (entry.getMap() == null ? null : new Mapping(entry.getMap())));
+            (entry.getMap() == null ? null : new Mapping(entry.getMap())),entry.isCanonical());
   }
 
   @Override
@@ -156,6 +174,7 @@ public class DBRefEntry implements DBRefEntryI
       return true;
     }
 
+    boolean improved=false;
     /*
      * source must either match or be both null
      */
@@ -179,6 +198,19 @@ public class DBRefEntry implements DBRefEntryI
       return false;
     }
 
+    if (!isCanonicalAccession && other.isCanonical())
+    {
+      isCanonicalAccession = true;
+      improved = true;
+    }
+    else
+    {
+      if (isCanonicalAccession && !other.isCanonical())
+      {
+        // other is not an authoritative source of canonical accessions
+        return false;
+      }
+    }
     /*
      * if my version is null, "0" or "source:0" then replace with other version,
      * otherwise the versions have to match
@@ -195,12 +227,15 @@ public class DBRefEntry implements DBRefEntryI
       if (version != null && (otherVersion == null
               || !version.equalsIgnoreCase(otherVersion)))
       {
-        return false;
+        // FIXME: there may be a problem with old version strings not allowing
+        // updating of dbrefentries
+        return improved;
       }
     }
 
     /*
-     * if I have no mapping, take that of the other dbref
+     * if I have no mapping, take that of the other dbref 
+     * - providing it had a version and so do I
      */
     if (map == null)
     {
@@ -273,7 +308,7 @@ public class DBRefEntry implements DBRefEntryI
   public void setAccessionId(String accessionId)
   {
          this.accessionId = accessionId;
-//    this.accessionId = (accessionId == null ? "" : accessionId).toUpperCase();
+//    this.accessionId = (accessionId == null ? "" : accessionId).toUpperCase(Locale.ROOT);
   }
 
   /**
@@ -284,7 +319,7 @@ public class DBRefEntry implements DBRefEntryI
   {
          this.source = source;
          
-//    this.source = (source == null ? "" : source).toUpperCase();
+//    this.source = (source == null ? "" : source).toUpperCase(Locale.ROOT);
 //    this.canonicalSourceName =       DBRefUtils.getCanonicalName(this.source);
 //    this.sourceKey = DBRefSource.getSourceKey(this.canonicalSourceName);
   }
@@ -293,7 +328,7 @@ public class DBRefEntry implements DBRefEntryI
   public void setVersion(String version)
   {
     this.version = version;
-    this.ucversion = (version == null ? null : version.toUpperCase());
+    this.ucversion = (version == null ? null : version.toUpperCase(Locale.ROOT));
   }
 
   @Override
@@ -383,4 +418,22 @@ public class DBRefEntry implements DBRefEntryI
   public String getCanonicalSourceName() {
        return (canonicalSourceName == null ? (canonicalSourceName = DBRefUtils.getCanonicalName(this.source)) : canonicalSourceName);
   }
+
+  /**
+   * 
+   * @param canonical
+   */
+  public void setCanonical(boolean canonical)
+  {
+    isCanonicalAccession = canonical;
+  }
+  /**
+   * 
+   * @return true if this is the primary canonical accession for the database source
+   */
+  public boolean isCanonical()
+  {
+    // TODO Auto-generated method stub
+    return isCanonicalAccession;
+  }
 }
index 2f94884..2d2ae4f 100755 (executable)
@@ -36,29 +36,31 @@ package jalview.datamodel;
  * @author JimP
  * 
  */
+import java.util.Locale;
+
 public class DBRefSource
 {
   
   
   
   public static final String UNIPROT = "UNIPROT";
-  public static final String UP_NAME = "UNIPROT_NAME".toUpperCase();
+  public static final String UP_NAME = "UNIPROT_NAME".toUpperCase(Locale.ROOT);
   /**
    * Uniprot Knowledgebase/TrEMBL as served from EMBL protein products.
    */
-  public static final String UNIPROTKB = "UniProtKB/TrEMBL".toUpperCase();
+  public static final String UNIPROTKB = "UniProtKB/TrEMBL".toUpperCase(Locale.ROOT);
 
   public static final String ENSEMBL        = "ENSEMBL";
   public static final String ENSEMBLGENOMES = "ENSEMBLGENOMES";
   
   public static final String EMBL           = "EMBL";
   public static final String EMBLCDS        = "EMBLCDS";
-  public static final String EMBLCDSProduct = "EMBLCDSProtein".toUpperCase();
+  public static final String EMBLCDSProduct = "EMBLCDSProtein".toUpperCase(Locale.ROOT);
 
   public static final String PDB    = "PDB";
   public static final String PFAM   = "PFAM";
   public static final String RFAM   = "RFAM";
-  public static final String GENEDB = "GeneDB".toUpperCase();
+  public static final String GENEDB = "GeneDB".toUpperCase(Locale.ROOT);
 
   public static final String PDB_CANONICAL_NAME = PDB;
 
@@ -146,7 +148,7 @@ public class DBRefSource
     // see if there is a primary reference that derived this reference.
     for (int i = allSources.length; --i >= 0;)
     {
-      if (ucversion.startsWith(allSources[i])) // BH 2019.01.25 .toUpperCase() unnecessary here for allSources
+      if (ucversion.startsWith(allSources[i])) // BH 2019.01.25 .toUpperCase(Locale.ROOT) unnecessary here for allSources
       {
         // by convention, many secondary references inherit the primary
         // reference's
index d652a97..f145d93 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.datamodel;
 
+import java.util.Locale;
+
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -199,12 +201,12 @@ public class MappedFeatures
      * e.g. C,G,T gives variants G and T for base C
      */
     Set<String> variantPeptides = new HashSet<>();
-    String[] alleles = alls.toUpperCase().split(",");
+    String[] alleles = alls.toUpperCase(Locale.ROOT).split(",");
     StringBuilder vars = new StringBuilder();
 
     for (String allele : alleles)
     {
-      allele = allele.trim().toUpperCase();
+      allele = allele.trim().toUpperCase(Locale.ROOT);
       if (allele.length() > 1 || "-".equals(allele))
       {
         continue; // multi-locus variant
@@ -220,7 +222,7 @@ public class MappedFeatures
        */
       final int i = cdsPos == codonPos[0] ? 0
               : (cdsPos == codonPos[1] ? 1 : 2);
-      variantCodon[i] = allele.toUpperCase().charAt(0);
+      variantCodon[i] = allele.toUpperCase(Locale.ROOT).charAt(0);
       if (variantCodon[i] == baseCodon[i])
       {
         continue;
index b5184fb..4d90e3e 100644 (file)
  */
 package jalview.datamodel;
 
-import jalview.util.Comparison;
-import jalview.util.MapList;
-
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.Vector;
 
+import jalview.util.Comparison;
+import jalview.util.MapList;
+
 public class Mapping
 {
   /**
@@ -433,23 +433,6 @@ public class Mapping
   }
 
   /**
-   * gets boundary in direction of mapping
-   * 
-   * @param position
-   *          in mapped reference frame
-   * @return int{start, end} positions in associated sequence (in direction of
-   *         mapped word)
-   */
-  public int[] getWord(int mpos)
-  {
-    if (map != null)
-    {
-      return map.getToWord(mpos);
-    }
-    return null;
-  }
-
-  /**
    * width of mapped unit in associated sequence
    * 
    */
index c1dc77c..fc4ee46 100755 (executable)
@@ -38,6 +38,8 @@ public class PDBEntry
 
   private static final int PDB_ID_LENGTH = 4;
 
+  private static final String FAKED_ID = "faked_pdbid";
+
   private String file;
 
   private String type;
@@ -384,21 +386,40 @@ public class PDBEntry
       return false; // shouldn't happen
     }
 
-    /*
-     * id has to match (ignoring case)
-     */
-    if (!getId().equalsIgnoreCase(newId))
-    {
-      return false;
-    }
+    boolean idMatches = getId().equalsIgnoreCase(newId);
 
     /*
      * Don't update if associated with different structure files
      */
     String newFile = newEntry.getFile();
-    if (newFile != null && getFile() != null && !newFile.equals(getFile()))
+    if (newFile != null && getFile() != null)
     {
-      return false;
+      if (!newFile.equals(getFile()))
+      {
+        return false;
+      }
+      else
+      {
+        // files match.
+        if (!idMatches)
+        {
+          // this shouldn't happen, but could do if the id from the
+          // file is not the same as the id from the authority that provided
+          // the file
+          if (!newEntry.fakedPDBId())
+          {
+            return false;
+          } // otherwise we can update
+        }
+      }
+    }
+    else
+    {
+      // one has data, one doesn't ..
+      if (!idMatches)
+      {
+        return false;
+      } // otherwise maybe can update
     }
 
     /*
@@ -453,6 +474,11 @@ public class PDBEntry
        */
       String key = newProps.nextElement();
       Object value = newEntry.getProperty(key);
+      if (FAKED_ID.equals(key))
+      {
+        // we never update the fake ID property
+        continue;
+      }
       if (!value.equals(getProperty(key)))
       {
         setProperty(key, value);
@@ -460,4 +486,111 @@ public class PDBEntry
     }
     return true;
   }
+  
+  /**
+   * set when Jalview has manufactured the ID using a local filename
+   * @return
+   */
+  public boolean fakedPDBId()
+  {
+    if (_hasProperty(FAKED_ID))
+    {
+      return true;
+    }
+    return false;
+  }
+  public void setFakedPDBId(boolean faked)
+  {
+    if (faked)
+    {
+      setProperty(FAKED_ID, Boolean.TRUE);
+    }
+    else 
+    {
+      if (properties!=null) {
+        properties.remove(FAKED_ID);
+      }
+    }
+  }
+
+  private boolean _hasProperty(final String key)
+  {
+    return (properties != null && properties.containsKey(key));
+  }
+
+  private static final String RETRIEVE_FROM = "RETRIEVE_FROM";
+
+  private static final String PROVIDER = "PROVIDER";
+
+  private static final String MODELPAGE = "PROVIDERPAGE";
+
+  /**
+   * Permanent URI for retrieving the original structure data
+   * 
+   * @param urlStr
+   */
+  public void setRetrievalUrl(String urlStr)
+  {
+    setProperty(RETRIEVE_FROM, urlStr);
+  }
+
+  public boolean hasRetrievalUrl()
+  {
+    return _hasProperty(RETRIEVE_FROM);
+  }
+
+  /**
+   * get the Permanent URI for retrieving the original structure data
+   */
+  public String getRetrievalUrl()
+  {
+    return (String) getProperty(RETRIEVE_FROM);
+  }
+
+  /**
+   * Data provider name - from 3D Beacons
+   * 
+   * @param provider
+   */
+  public void setProvider(String provider)
+  {
+    setProperty(PROVIDER, provider);
+  }
+
+  /**
+   * Get Data provider name - from 3D Beacons
+   * 
+   */
+  public String getProvider()
+  {
+    return (String) getProperty(PROVIDER);
+  }
+
+  /**
+   * Permanent URI for retrieving the original structure data
+   * 
+   * @param urlStr
+   */
+  public void setProviderPage(String urlStr)
+  {
+    setProperty(MODELPAGE, urlStr);
+  }
+
+  /**
+   * get the Permanent URI for retrieving the original structure data
+   */
+  public String getProviderPage()
+  {
+    return (String) getProperty(MODELPAGE);
+  }
+
+  public boolean hasProviderPage()
+  {
+    return _hasProperty(MODELPAGE);
+  }
+
+  public boolean hasProvider()
+  {
+    return _hasProperty(PROVIDER);
+  }
 }
index 74eb887..efab97c 100644 (file)
@@ -70,7 +70,7 @@ public class ResidueCount
    */
   private static final String AAS = "ACDEFGHIKLMNPQRSTUVWXY";
 
-  private static final int GAP_COUNT = 0;
+  static final int GAP_COUNT = 0;
 
   /*
    * fast lookup tables holding the index into our count
@@ -212,7 +212,12 @@ public class ResidueCount
         counts[offset] = (short) ++newValue;
       }
     }
-    maxCount = Math.max(maxCount, newValue);
+
+    if (offset!=GAP_COUNT)
+    {
+      // update modal residue count
+      maxCount = Math.max(maxCount, newValue);
+    }
     return newValue;
   }
 
@@ -301,15 +306,7 @@ public class ResidueCount
    */
   public int addGap()
   {
-    int newValue;
-    if (useIntCounts)
-    {
-      newValue = ++intCounts[GAP_COUNT];
-    }
-    else
-    {
-      newValue = ++counts[GAP_COUNT];
-    }
+    int newValue = increment(GAP_COUNT);
     return newValue;
   }
 
index 552349f..d52e049 100755 (executable)
@@ -1799,13 +1799,30 @@ public class Sequence extends ASequence implements SequenceI
   public List<AlignmentAnnotation> getAlignmentAnnotations(String calcId,
           String label)
   {
+    return getAlignmentAnnotations(calcId, label, null, true);
+  }
+
+  @Override
+  public List<AlignmentAnnotation> getAlignmentAnnotations(String calcId,
+          String label, String description)
+  {
+    return getAlignmentAnnotations(calcId, label, description, false);
+  }
+
+  private List<AlignmentAnnotation> getAlignmentAnnotations(String calcId,
+          String label, String description, boolean ignoreDescription)
+  {
     List<AlignmentAnnotation> result = new ArrayList<>();
     if (this.annotation != null)
     {
       for (AlignmentAnnotation ann : annotation)
       {
-        if (ann.calcId != null && ann.calcId.equals(calcId)
-                && ann.label != null && ann.label.equals(label))
+        if ((ann.calcId != null && ann.calcId.equals(calcId))
+                && (ann.label != null && ann.label.equals(label))
+                && ((ignoreDescription && description == null)
+                        || (ann.description != null
+                                && ann.description.equals(description))))
+
         {
           result.add(ann);
         }
index 933f332..7c3eb41 100755 (executable)
@@ -446,6 +446,17 @@ public interface SequenceI extends ASequenceI
           String label);
 
   /**
+   * Returns a (possibly empty) list of any annotations that match on given
+   * calcId (source), label (type) and description (observation instance).
+   * Null values do not match.
+   * 
+   * @param calcId
+   * @param label
+   * @param description
+   */
+  public List<AlignmentAnnotation> getAlignmentAnnotations(String calcId,
+          String label, String description);
+  /**
    * create a new dataset sequence (if necessary) for this sequence and sets
    * this sequence to refer to it. This call will move any features or
    * references on the sequence onto the dataset. It will also make a duplicate
index e9fb9b2..69f80f2 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.datamodel.features;
 
+import java.util.Locale;
+
 import jalview.datamodel.SequenceFeature;
 import jalview.util.MessageManager;
 import jalview.util.matcher.Condition;
@@ -163,12 +165,12 @@ public class FeatureMatcher implements FeatureMatcherI
       firstField = descriptor.substring(0, nextSpacePos);
       leftToParse = descriptor.substring(nextSpacePos + 1).trim();
     }
-    String lower = firstField.toLowerCase();
-    if (lower.startsWith(LABEL.toLowerCase()))
+    String lower = firstField.toLowerCase(Locale.ROOT);
+    if (lower.startsWith(LABEL.toLowerCase(Locale.ROOT)))
     {
       byLabel = true;
     }
-    else if (lower.startsWith(SCORE.toLowerCase()))
+    else if (lower.startsWith(SCORE.toLowerCase(Locale.ROOT)))
     {
       byScore = true;
     }
@@ -351,7 +353,7 @@ public class FeatureMatcher implements FeatureMatcherI
     }
 
     Condition condition = matcher.getCondition();
-    sb.append(SPACE).append(condition.toString().toLowerCase());
+    sb.append(SPACE).append(condition.toString().toLowerCase(Locale.ROOT));
     if (condition.isNumeric())
     {
       sb.append(SPACE).append(matcher.getPattern());
index 3743278..ee4bf12 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.datamodel.features;
 
+import java.util.Locale;
+
 import jalview.datamodel.SequenceFeature;
 import jalview.util.MessageManager;
 
@@ -257,7 +259,7 @@ public class FeatureMatcherSet implements FeatureMatcherSetI
       if (!first)
       {
         String joiner = andConditions ? AND_18N : OR_I18N;
-        sb.append(SPACE).append(joiner.toLowerCase()).append(SPACE);
+        sb.append(SPACE).append(joiner.toLowerCase(Locale.ROOT)).append(SPACE);
       }
       first = false;
       if (multiple)
index 0e3d84b..ac756fc 100644 (file)
@@ -628,10 +628,10 @@ public class EnsemblGene extends EnsemblSeqProxy
       SequenceOntologyI so = SequenceOntologyFactory.getInstance();
 
       @Override
-      public boolean isFeatureDisplayed(String type)
+      public boolean isFeatureHidden(String type)
       {
-        return (so.isA(type, SequenceOntologyI.EXON)
-                || so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT));
+        return (!so.isA(type, SequenceOntologyI.EXON)
+                && !so.isA(type, SequenceOntologyI.SEQUENCE_VARIANT));
       }
 
       @Override
index 97ad242..e2d2725 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ext.ensembl;
 
+import java.util.Locale;
+
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
 import jalview.util.JSONUtils;
@@ -94,7 +96,7 @@ public class EnsemblInfo extends EnsemblRestClient
     {
       fetchDivisions();
     }
-    return divisions.get(division.toUpperCase());
+    return divisions.get(division.toUpperCase(Locale.ROOT));
   }
 
   /**
@@ -108,7 +110,7 @@ public class EnsemblInfo extends EnsemblRestClient
     /*
      * for convenience, pre-fill ensembl.org as the domain for "ENSEMBL"
      */
-    divisions.put(DBRefSource.ENSEMBL.toUpperCase(), ensemblDomain);
+    divisions.put(DBRefSource.ENSEMBL.toUpperCase(Locale.ROOT), ensemblDomain);
     try
     {
       @SuppressWarnings("unchecked")
@@ -118,7 +120,7 @@ public class EnsemblInfo extends EnsemblRestClient
       while (rvals.hasNext())
       {
         String division = rvals.next().toString();
-        divisions.put(division.toUpperCase(), ensemblGenomesDomain);
+        divisions.put(division.toUpperCase(Locale.ROOT), ensemblGenomesDomain);
       }
     } catch (IOException | ParseException | NumberFormatException e)
     {
index eee48df..903d9b7 100644 (file)
@@ -31,6 +31,8 @@ import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.Vector;
 
+import javax.swing.SwingUtilities;
+
 import org.jmol.adapter.smarter.SmarterJmolAdapter;
 import org.jmol.api.JmolAppConsoleInterface;
 import org.jmol.api.JmolSelectionListener;
@@ -41,10 +43,12 @@ import org.jmol.viewer.Viewer;
 
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
+import jalview.api.FeatureSettingsModelI;
 import jalview.api.SequenceRenderer;
 import jalview.bin.Cache;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
+import jalview.gui.AppJmol;
 import jalview.gui.IProgressIndicator;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.io.DataSourceType;
@@ -54,6 +58,7 @@ import jalview.structure.StructureCommand;
 import jalview.structure.StructureCommandI;
 import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
+import jalview.ws.dbsources.Pdb;
 import javajs.util.BS;
 
 public abstract class JalviewJmolBinding extends AAStructureBindingModel
@@ -86,8 +91,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     setStructureCommands(new JmolCommands());
     /*
      * viewer = JmolViewer.allocateViewer(renderPanel, new SmarterJmolAdapter(),
-     * "jalviewJmol", ap.av.applet .getDocumentBase(),
-     * ap.av.applet.getCodeBase(), "", this);
+     * "jalviewJmol", ap.av.applet .getDocumentBase(), ap.av.applet.getCodeBase(),
+     * "", this);
      * 
      * jmolpopup = JmolPopup.newJmolPopup(viewer, true, "Jmol", true);
      */
@@ -204,8 +209,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       for (int i = 0; i < modelCount; ++i)
       {
         /*
-         * defensive check for null as getModelFileName can return null
-         * even when model count ms.mc is > 0
+         * defensive check for null as getModelFileName can return null even when model
+         * count ms.mc is > 0
          */
         filePath = jmolViewer.ms.getModelFileName(i);
         if (filePath != null && !mset.contains(filePath))
@@ -398,8 +403,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
     }
 
     /*
-     * highlight position on alignment(s); if some text is returned, 
-     * show this as a second line on the structure hover tooltip
+     * highlight position on alignment(s); if some text is returned, show this as a
+     * second line on the structure hover tooltip
      */
     String label = getSsm().mouseOverStructure(pdbResNum, chainId,
             pdbfilename);
@@ -435,8 +440,8 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
 
   /*
    * { if (history != null && strStatus != null &&
-   * !strStatus.equals("Script completed")) { history.append("\n" + strStatus);
-   * } }
+   * !strStatus.equals("Script completed")) { history.append("\n" + strStatus); }
+   * }
    */
 
   public void notifyAtomPicked(int atomIndex, String strInfo,
@@ -497,6 +502,28 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   @Override
   public void notifyCallback(CBK type, Object[] data)
   {
+    /*
+     * ensure processed in AWT thread to avoid risk of deadlocks
+     */
+    SwingUtilities.invokeLater(new Runnable()
+    {
+
+      @Override
+      public void run()
+      {
+        processCallback(type, data);
+      }
+    });
+  }
+
+  /**
+   * Processes one callback notification from Jmol
+   * 
+   * @param type
+   * @param data
+   */
+  protected void processCallback(CBK type, Object[] data)
+  {
     try
     {
       switch (type)
@@ -745,7 +772,9 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
       FeatureRenderer fr = getFeatureRenderer(null);
       if (fr != null)
       {
-        fr.featuresAdded();
+        FeatureSettingsModelI colours = new Pdb().getFeatureColourScheme();
+        ((AppJmol) getViewer()).getAlignmentPanel().av
+                .applyFeaturesStyle(colours);
       }
       refreshGUI();
       loadNotifiesHandled++;
@@ -774,8 +803,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
   public abstract void sendConsoleEcho(String strEcho); /*
                                                          * { showConsole(true);
                                                          * 
-                                                         * history.append("\n" +
-                                                         * strEcho); }
+                                                         * history.append("\n" + strEcho); }
                                                          */
 
   // /End JmolStatusListener
index a9c5f5c..2a43244 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ext.jmol;
 
+import java.util.Locale;
+
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.PDBEntry;
@@ -45,6 +47,8 @@ import org.jmol.c.STR;
 import org.jmol.modelset.ModelSet;
 import org.jmol.viewer.Viewer;
 
+import com.stevesoft.pat.Regex;
+
 import mc_view.Atom;
 import mc_view.PDBChain;
 import mc_view.Residue;
@@ -59,6 +63,8 @@ public class JmolParser extends StructureFile implements JmolStatusListener
 {
   Viewer viewer = null;
 
+  private boolean alphaFoldModel;
+
   public JmolParser(boolean immediate, Object inFile,
           DataSourceType sourceType) throws IOException
   {
@@ -111,7 +117,7 @@ public class JmolParser extends StructureFile implements JmolStatusListener
       // }
       // ;
       // instead, we distinguish .cif from non-.cif by filename
-      setStructureFileType(getDataName().toLowerCase().endsWith(".cif")
+      setStructureFileType(getDataName().toLowerCase(Locale.ROOT).endsWith(".cif")
               ? PDBEntry.Type.MMCIF.toString()
               : "PDB");
 
@@ -148,17 +154,58 @@ public class JmolParser extends StructureFile implements JmolStatusListener
     }
     return viewer;
   }
+  
+  public static Regex getNewAlphafoldValidator()
+  {
+    Regex validator =  new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
+    validator.setIgnoreCase(true);
+    return validator;
+  }
 
+  PDBEntry.Type jmolFiletype=null;
+  /**
+   * resolve a jmol filetype string and update the jmolFiletype field accordingly
+   * @param jmolIdentifiedFileType
+   * @return true if filetype was identified as MMCIF, PDB
+   */
+  public boolean updateFileType(String jmolIdentifiedFileType)
+  {
+    if (jmolIdentifiedFileType == null 
+            || jmolIdentifiedFileType.trim().equals(""))
+    {
+      return false;
+    }
+    if ("mmcif".equalsIgnoreCase(jmolIdentifiedFileType)) {
+      jmolFiletype = PDBEntry.Type.MMCIF;
+      return true;
+    }
+    if ("pdb".equalsIgnoreCase(jmolIdentifiedFileType))
+    {
+      jmolFiletype = PDBEntry.Type.PDB;
+      return true;
+    } 
+    return false;
+  }
+  
   public void transformJmolModelToJalview(ModelSet ms) throws IOException
   {
     try
     {
+      Regex alphaFold = getNewAlphafoldValidator();
       String lastID = "";
       List<SequenceI> rna = new ArrayList<SequenceI>();
       List<SequenceI> prot = new ArrayList<SequenceI>();
       PDBChain tmpchain;
       String pdbId = (String) ms.getInfo(0, "title");
-
+      boolean isMMCIF = false;
+      String jmolFileType_String = (String) ms.getInfo(0, "fileType");
+      if (updateFileType(jmolFileType_String))
+      {
+        setStructureFileType(jmolFiletype.toString());
+      }
+      
+      isMMCIF = PDBEntry.Type.MMCIF.equals(jmolFiletype);
+      
       if (pdbId == null)
       {
         setId(safeName(getDataName()));
@@ -168,6 +215,8 @@ public class JmolParser extends StructureFile implements JmolStatusListener
       {
         setId(pdbId);
         setPDBIdAvailable(true);
+        alphaFoldModel = alphaFold.search(pdbId) && isMMCIF;  
+
       }
       List<Atom> significantAtoms = convertSignificantAtoms(ms);
       for (Atom tmpatom : significantAtoms)
@@ -183,7 +232,13 @@ public class JmolParser extends StructureFile implements JmolStatusListener
           tmpchain.atoms.addElement(tmpatom);
         } else
         {
-          tmpchain = new PDBChain(getId(), tmpatom.chain);
+          String tempFString=null;
+          if (isAlphafoldModel())
+          {
+            tempFString = "Alphafold Reliability";
+          }
+
+          tmpchain = new PDBChain(getId(), tmpatom.chain,tempFString);
           getChains().add(tmpchain);
           tmpchain.atoms.addElement(tmpatom);
         }
@@ -225,6 +280,11 @@ public class JmolParser extends StructureFile implements JmolStatusListener
     }
   }
 
+  private boolean isAlphafoldModel()
+  {
+    return alphaFoldModel;
+  }
+
   private List<Atom> convertSignificantAtoms(ModelSet ms)
   {
     List<Atom> significantAtoms = new ArrayList<Atom>();
@@ -351,7 +411,7 @@ public class JmolParser extends StructureFile implements JmolStatusListener
 
   /**
    * Helper method that adds an AlignmentAnnotation for secondary structure to
-   * the sequence, provided at least one secondary structure prediction has been
+   * the sequence, provided at least one secondary structure assignment has been
    * made
    * 
    * @param modelTitle
index ced22fa..02b7136 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ext.rbvi.chimera;
 
+import java.util.Locale;
+
 import java.awt.Color;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -181,7 +183,7 @@ public class ChimeraCommands extends StructureCommandsBase
      * Chimera treats an attribute name ending in 'color' as colour-valued;
      * Jalview doesn't, so prevent this by appending an underscore
      */
-    if (attName.toUpperCase().endsWith("COLOR"))
+    if (attName.toUpperCase(Locale.ROOT).endsWith("COLOR"))
     {
       attName += "_";
     }
index 314e6db..4e45ac8 100644 (file)
@@ -28,7 +28,6 @@ import java.util.List;
 import jalview.structure.AtomSpecModel;
 import jalview.structure.StructureCommand;
 import jalview.structure.StructureCommandI;
-import jalview.util.ColorUtils;
 
 /**
  * Routines for generating ChimeraX commands for Jalview/ChimeraX binding
@@ -36,15 +35,19 @@ import jalview.util.ColorUtils;
 public class ChimeraXCommands extends ChimeraCommands
 {
   // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/info.html#resattr
-  private static final StructureCommand LIST_RESIDUE_ATTRIBUTES = new StructureCommand("info resattr");
+  private static final StructureCommand LIST_RESIDUE_ATTRIBUTES = new StructureCommand(
+          "info resattr");
 
   // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/exit.html
-  private static final StructureCommand CLOSE_CHIMERAX = new StructureCommand("exit");
+  private static final StructureCommand CLOSE_CHIMERAX = new StructureCommand(
+          "exit");
 
   // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/info.html#notify
-  private static final StructureCommand STOP_NOTIFY_SELECTION = new StructureCommand("info notify stop selection jalview");
+  private static final StructureCommand STOP_NOTIFY_SELECTION = new StructureCommand(
+          "info notify stop selection jalview");
 
-  private static final StructureCommand STOP_NOTIFY_MODELS = new StructureCommand("info notify stop models jalview");
+  private static final StructureCommand STOP_NOTIFY_MODELS = new StructureCommand(
+          "info notify stop models jalview");
 
   // https://www.cgl.ucsf.edu/chimerax/docs/user/commands/info.html#selection
   private static final StructureCommand GET_SELECTION = new StructureCommand(
@@ -99,8 +102,8 @@ public class ChimeraXCommands extends ChimeraCommands
   }
 
   /**
-   * Returns a viewer command to set the given residue attribute value on
-   * residues specified by the AtomSpecModel, for example
+   * Returns a viewer command to set the given residue attribute value on residues
+   * specified by the AtomSpecModel, for example
    * 
    * <pre>
    * setattr #0/A:3-9,14-20,39-43 res jv_strand 'strand' create true
@@ -142,6 +145,10 @@ public class ChimeraXCommands extends ChimeraCommands
    * Returns the range(s) formatted as a ChimeraX atomspec, for example
    * <p>
    * #1/A:2-20,30-40/B:10-20|#2/A:12-30
+   * <p>
+   * Note there is no need to explicitly exclude ALTLOC atoms when
+   * {@code alphaOnly == true}, as this is the default behaviour of ChimeraX (a
+   * change from Chimera)
    * 
    * @return
    */
@@ -163,7 +170,6 @@ public class ChimeraXCommands extends ChimeraCommands
         // TODO @P if RNA - add nucleotide flag to AtomSpecModel?
         sb.append("@CA");
       }
-      // todo: is there ChimeraX syntax to exclude altlocs?
     }
     return sb.toString();
   }
@@ -250,8 +256,12 @@ public class ChimeraXCommands extends ChimeraCommands
   public List<StructureCommandI> startNotifications(String uri)
   {
     List<StructureCommandI> cmds = new ArrayList<>();
-    cmds.add(new StructureCommand("info notify start models prefix ModelChanged jalview url " + uri));
-    cmds.add(new StructureCommand("info notify start selection jalview prefix SelectionChanged url " + uri));
+    cmds.add(new StructureCommand(
+            "info notify start models jalview prefix ModelChanged url "
+                    + uri));
+    cmds.add(new StructureCommand(
+            "info notify start selection jalview prefix SelectionChanged url "
+                    + uri));
     return cmds;
   }
 
index 33b0ed6..31d5c7c 100644 (file)
@@ -22,6 +22,7 @@
 package jalview.fts.api;
 
 import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
 import jalview.fts.core.FTSRestRequest;
 import jalview.fts.core.FTSRestResponse;
 
@@ -88,7 +89,7 @@ public interface FTSRestClientI
 
   /**
    * Fetch index of the primary key column for the dynamic table
-   * 
+   * TODO: consider removing 'hasRefSeq' - never used in code
    * @param wantedFields
    *          the available table columns
    * @param hasRefSeq
@@ -136,4 +137,6 @@ public interface FTSRestClientI
    * @return the default response page size
    */
   public int getDefaultResponsePageSize();
+
+  public String[] getPreferencesColumnsFor(PreferenceSource source);
 }
diff --git a/src/jalview/fts/api/StructureFTSRestClientI.java b/src/jalview/fts/api/StructureFTSRestClientI.java
new file mode 100644 (file)
index 0000000..4974b80
--- /dev/null
@@ -0,0 +1,10 @@
+package jalview.fts.api;
+
+import java.util.Collection;
+
+public interface StructureFTSRestClientI
+{
+
+  Collection<FTSDataColumnI> getAllDefaultDisplayedStructureDataColumns();
+
+}
index e5042ae..0335d65 100644 (file)
@@ -23,6 +23,7 @@ package jalview.fts.core;
 import jalview.fts.api.FTSDataColumnI;
 import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
 import jalview.fts.api.FTSRestClientI;
+import jalview.fts.api.StructureFTSRestClientI;
 import jalview.fts.service.pdb.PDBFTSRestClient;
 
 import java.util.ArrayList;
@@ -39,7 +40,13 @@ import javax.swing.SortOrder;
 import javax.swing.table.AbstractTableModel;
 import javax.swing.table.TableModel;
 import javax.swing.table.TableRowSorter;
-
+/**
+ * Helps render GUI allowing control of which columns to show for entries returned from an FTS query.
+ * TODO: push down FTSClient specific code
+ * 
+ * @author tcofoegbu
+ *
+ */
 @SuppressWarnings("serial")
 public class FTSDataColumnPreferences extends JScrollPane
 {
@@ -70,7 +77,7 @@ public class FTSDataColumnPreferences extends JScrollPane
     if (source.equals(PreferenceSource.STRUCTURE_CHOOSER)
             || source.equals(PreferenceSource.PREFERENCES))
     {
-      structSummaryColumns = ((PDBFTSRestClient) ftsRestClient)
+      structSummaryColumns = ((StructureFTSRestClientI) ftsRestClient)
               .getAllDefaultDisplayedStructureDataColumns();
     }
     allFTSDataColumns.addAll(ftsRestClient.getAllFTSDataColumns());
@@ -79,28 +86,14 @@ public class FTSDataColumnPreferences extends JScrollPane
     this.getViewport().add(tbl_FTSDataColumnPrefs);
     this.currentSource = source;
 
-    String[] columnNames = null;
-    switch (source)
-    {
-    case SEARCH_SUMMARY:
-      columnNames = new String[] { "", "Display", "Group" };
-      break;
-    case STRUCTURE_CHOOSER:
-      columnNames = new String[] { "", "Display", "Group" };
-      break;
-    case PREFERENCES:
-      columnNames = new String[] { "PDB Field", "Show in search summary",
-          "Show in structure summary" };
-      break;
-    default:
-      break;
-    }
+    String[] columnNames = ftsRestClient.getPreferencesColumnsFor(source);
 
-    Object[][] data = new Object[allFTSDataColumns.size() - 1][3];
+    Object[][] data = new Object[allFTSDataColumns.size()][3];
 
     int x = 0;
     for (FTSDataColumnI field : allFTSDataColumns)
-    {
+    {   
+      //System.out.println("allFTSDataColumns==" + allFTSDataColumns);
       if (field.getName().equalsIgnoreCase("all"))
       {
         continue;
@@ -112,6 +105,7 @@ public class FTSDataColumnPreferences extends JScrollPane
         data[x++] = new Object[] { ftsRestClient
                 .getAllDefaultDisplayedFTSDataColumns().contains(field),
             field.getName(), field.getGroup() };
+        //System.out.println(" PUIS " + field.getName() + " ET AUSSI " + field.getGroup() + "X = " + x);
         break;
       case STRUCTURE_CHOOSER:
         data[x++] = new Object[] { structSummaryColumns.contains(field),
index 7a8a695..0bca070 100644 (file)
  */
 package jalview.fts.core;
 
+import java.util.Locale;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Objects;
 
 import jalview.fts.api.FTSDataColumnI;
 import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient;
 import jalview.fts.api.FTSRestClientI;
 
 /**
@@ -56,6 +61,8 @@ public abstract class FTSRestClient implements FTSRestClientI
 
   private int defaultResponsePageSize = 100;
 
+  protected HashMap<String,String> mockQueries = null;
+
   protected FTSRestClient()
   {
 
@@ -166,7 +173,7 @@ public abstract class FTSRestClient implements FTSRestClientI
               public DataTypeI getDataType()
               {
                 final String[] dataTypeString = lineData[2].split("\\|");
-                final String classString = dataTypeString[0].toUpperCase();
+                final String classString = dataTypeString[0].toUpperCase(Locale.ROOT);
 
                 return new DataTypeI()
                 {
@@ -177,7 +184,7 @@ public abstract class FTSRestClient implements FTSRestClientI
                     if (dataTypeString.length > 1
                             && dataTypeString[1] != null)
                     {
-                      switch (dataTypeString[1].toUpperCase())
+                      switch (dataTypeString[1].toUpperCase(Locale.ROOT))
                       {
                       case "T":
                       case "TRUE":
@@ -427,6 +434,16 @@ public abstract class FTSRestClient implements FTSRestClientI
             "Couldn't find data column with name : " + nameOrCode);
   }
 
+  /**
+   * 
+   * @param instance
+   * @param mocks {{working query, working response}, ...}
+   */
+  public static void createMockFTSRestClient(FTSRestClient instance,String[][] mocks)
+  {
+    instance.setMock(mocks);
+  }
+
   @Override
   public FTSDataColumnGroupI getDataColumnGroupById(String id)
           throws Exception
@@ -480,6 +497,11 @@ public abstract class FTSRestClient implements FTSRestClientI
     return String.valueOf(code) + " " + message;
   }
 
+  public static void unMock(FTSRestClient instance)
+  {
+    instance.mockQueries=null;
+  }
+
   protected String getResourceFile(String fileName)
   {
     String result = "";
@@ -504,4 +526,37 @@ public abstract class FTSRestClient implements FTSRestClientI
     return defaultResponsePageSize;
   }
 
+  protected void setMock(String[][] mocks)
+  {
+    if (mocks==null) {
+      mockQueries=null;
+      return;
+    }
+    mockQueries=new HashMap<String,String>();
+    for (String[] mock:mocks)
+    {
+      mockQueries.put(mock[0],mock[1]);
+    }
+  }
+
+  protected boolean isMocked()
+  {
+    return mockQueries!=null;
+  }
+
+  @Override
+  public String[] getPreferencesColumnsFor(PreferenceSource source)
+  {
+    String[] columnNames = null;
+    switch (source)
+    {
+    case SEARCH_SUMMARY:
+      columnNames = new String[] { "", "Display", "Group" };
+      break;
+    default:
+      // non structure sources don't return any other kind of preferences columns
+      break;
+    }
+    return columnNames;
+  }
 }
index 2d9eeb6..bf88d2d 100644 (file)
@@ -188,4 +188,23 @@ public class FTSRestRequest
   {
     this.offSet = offSet;
   }
+
+  /**
+   * locate column given field name
+   * @param string - field name
+   * @return -1 if not located
+   */
+  public int getFieldIndex(String string)
+  {
+    int i=associatedSequence!=null ? 1 : 0;
+    for (FTSDataColumnI field:wantedFields)
+    {
+      if (field.getName().equals(string))
+      {
+        return i; 
+      }
+      i++;
+    }
+    return -1;
+  }
 }
index 597bb89..ae50233 100644 (file)
@@ -41,7 +41,7 @@ import javax.swing.table.DefaultTableModel;
 public class FTSRestResponse
 {
   private int numberOfItemsFound;
-
+  
   private String responseTime;
 
   private Collection<FTSData> searchSummary;
diff --git a/src/jalview/fts/service/alphafold/AlphafoldRestClient.java b/src/jalview/fts/service/alphafold/AlphafoldRestClient.java
new file mode 100644 (file)
index 0000000..6b855fc
--- /dev/null
@@ -0,0 +1,157 @@
+package jalview.fts.service.alphafold;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.util.DBRefUtils;
+import jalview.util.HttpUtils;
+import jalview.ws.dbsources.EBIAlfaFold;
+
+public class AlphafoldRestClient
+{
+
+  /**
+   * turns a uniprot ID into a fake alphafold entry for the structure chooser -
+   * fakes PDB fields in response
+   * 
+   * @param UniprotID
+   * @return null or an FTS Record (if alphafold thinks it has a structure)
+   */
+  public static List<FTSData> getFTSData(// Map<String, Object> pdbJsonDoc,
+          FTSRestRequest request)
+  {
+    List<FTSData> records = new ArrayList<FTSData>();
+    String primaryKey = null;
+
+    Object[] summaryRowData;
+
+    SequenceI associatedSequence;
+
+    Collection<FTSDataColumnI> diplayFields = request.getWantedFields();
+    SequenceI associatedSeq = request.getAssociatedSequence();
+
+    for (DBRefEntry upref : DBRefUtils
+            .selectRefs(associatedSeq.getPrimaryDBRefs(), new String[]
+            { DBRefSource.UNIPROT }))
+    {
+      String alphaFoldId = "AF-" + upref.getAccessionId() + "-F1";
+      try
+      {
+        String urls = EBIAlfaFold.getAlphaFoldCifDownloadUrl(alphaFoldId);
+        URL url = new URL(urls);
+        if (!HttpUtils.checkUrlAvailable(url, 50))
+        {
+          continue;
+        }
+      } catch (Exception mfe)
+      {
+        jalview.bin.Cache.log.debug("Exception accessing urls", mfe);
+        continue;
+      }
+      int colCounter = 0;
+      summaryRowData = new Object[(associatedSeq != null)
+              ? diplayFields.size() + 1
+              : diplayFields.size()];
+      if (associatedSeq != null)
+      {
+        associatedSequence = associatedSeq;
+        summaryRowData[0] = associatedSequence;
+        colCounter = 1;
+      }
+
+      for (FTSDataColumnI field : diplayFields)
+      {
+        String fieldData = "alphafold";// (pdbJsonDoc.get(field.getCode()) ==
+                                       // null) ? ""
+        // : pdbJsonDoc.get(field.getCode()).toString();
+        if (field.isPrimaryKeyColumn())
+        {
+          primaryKey = alphaFoldId;
+          summaryRowData[colCounter++] = alphaFoldId;
+        }
+        else if (fieldData == null || fieldData.isEmpty())
+        {
+          summaryRowData[colCounter++] = null;
+        }
+        else
+        {
+          try
+          {
+            summaryRowData[colCounter++] = (field.getDataType()
+                    .getDataTypeClass() == Integer.class)
+                            ? 1
+                            : (field.getDataType()
+                                    .getDataTypeClass() == Double.class)
+                                            ? 1.3131313
+                                            : "AlphaFold clarity";
+          } catch (Exception e)
+          {
+            e.printStackTrace();
+            System.out.println("offending value:" + fieldData);
+          }
+        }
+      }
+
+      final String primaryKey1 = primaryKey;
+
+      final Object[] summaryRowData1 = summaryRowData;
+      records.add(new FTSData()
+      {
+        @Override
+        public Object[] getSummaryData()
+        {
+          return summaryRowData1;
+        }
+
+        @Override
+        public Object getPrimaryKey()
+        {
+          return primaryKey1;
+        }
+
+        /**
+         * Returns a string representation of this object;
+         */
+        @Override
+        public String toString()
+        {
+          StringBuilder summaryFieldValues = new StringBuilder();
+          for (Object summaryField : summaryRowData1)
+          {
+            summaryFieldValues.append(
+                    summaryField == null ? " " : summaryField.toString())
+                    .append("\t");
+          }
+          return summaryFieldValues.toString();
+        }
+
+        /**
+         * Returns hash code value for this object
+         */
+        @Override
+        public int hashCode()
+        {
+          return Objects.hash(primaryKey1, this.toString());
+        }
+
+        @Override
+        public boolean equals(Object that)
+        {
+          return this.toString().equals(that.toString());
+        }
+      });
+    }
+    return records;
+  }
+}
index 22ed591..796bc0e 100644 (file)
  */
 package jalview.fts.service.pdb;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
 import java.net.URI;
+import java.nio.CharBuffer;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
@@ -41,9 +45,13 @@ import jalview.datamodel.SequenceI;
 import jalview.fts.api.FTSData;
 import jalview.fts.api.FTSDataColumnI;
 import jalview.fts.api.FTSRestClientI;
+import jalview.fts.api.StructureFTSRestClientI;
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
 import jalview.fts.core.FTSRestClient;
 import jalview.fts.core.FTSRestRequest;
 import jalview.fts.core.FTSRestResponse;
+import jalview.fts.service.alphafold.AlphafoldRestClient;
 import jalview.util.JSONUtils;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
@@ -54,6 +62,7 @@ import jalview.util.Platform;
  * @author tcnofoegbu
  */
 public class PDBFTSRestClient extends FTSRestClient
+        implements StructureFTSRestClientI
 {
 
   private static FTSRestClientI instance = null;
@@ -121,8 +130,9 @@ public class PDBFTSRestClient extends FTSRestClient
 
       // Build request parameters for the REST Request
 
-      // BH 2018 the trick here is to coerce the classes in Javascript to be 
-      // different from the ones in Java yet still allow this to be correct for Java
+      // BH 2018 the trick here is to coerce the classes in Javascript to be
+      // different from the ones in Java yet still allow this to be correct for
+      // Java
       Client client;
       Class<ClientResponse> clientResponseClass;
       if (Platform.isJS())
@@ -165,35 +175,61 @@ public class PDBFTSRestClient extends FTSRestClient
 
       URI uri = webResource.getURI();
 
-      // System.out.println(uri);
-
-      // Execute the REST request
-      ClientResponse clientResponse = webResource
-              .accept(MediaType.APPLICATION_JSON).get(clientResponseClass );
-
+      System.out.println(uri);
+      ClientResponse clientResponse = null;
+      int responseStatus = -1;
       // Get the JSON string from the response object or directly from the
       // client (JavaScript)
       Map<String, Object> jsonObj = null;
       String responseString = null;
 
-      // System.out.println("query >>>>>>> " + pdbRestRequest.toString());
+      System.out.println("query >>>>>>> " + pdbRestRequest.toString());
+
+      if (!isMocked())
+      {
+        // Execute the REST request
+        clientResponse = webResource.accept(MediaType.APPLICATION_JSON)
+                .get(clientResponseClass);
+        responseStatus = clientResponse.getStatus();
+      }
+      else
+      {
+        // mock response
+        if (mockQueries.containsKey(uri.toString()))
+        {
+          responseStatus = 200;
+        }
+        else
+        {
+          // FIXME - may cause unexpected exceptions for callers when mocked
+          responseStatus = 400;
+        }
+      }
 
       // Check the response status and report exception if one occurs
-      int responseStatus = clientResponse.getStatus();
       switch (responseStatus)
       {
       case 200:
-        if (Platform.isJS())
+
+        if (isMocked())
         {
-          jsonObj = clientResponse.getEntity(Map.class);
+          responseString = mockQueries.get(uri.toString());
         }
         else
         {
-          responseString = clientResponse.getEntity(String.class);
+          if (Platform.isJS())
+          {
+            jsonObj = clientResponse.getEntity(Map.class);
+          }
+          else
+          {
+            responseString = clientResponse.getEntity(String.class);
+          }
         }
         break;
       case 400:
-        throw new Exception(parseJsonExceptionString(responseString));
+        throw new Exception(isMocked() ? "400 response (Mocked)"
+                : parseJsonExceptionString(responseString));
       default:
         throw new Exception(
                 getMessageByHTTPStatusCode(responseStatus, "PDB"));
@@ -203,6 +239,10 @@ public class PDBFTSRestClient extends FTSRestClient
       return parsePDBJsonResponse(responseString, jsonObj, pdbRestRequest);
     } catch (Exception e)
     {
+      if (e.getMessage() == null)
+      {
+        throw (e);
+      }
       String exceptionMsg = e.getMessage();
       if (exceptionMsg.contains("SocketException"))
       {
@@ -231,39 +271,42 @@ public class PDBFTSRestClient extends FTSRestClient
    * @return the processed error message from the JSON string
    */
   @SuppressWarnings("unchecked")
-public static String parseJsonExceptionString(String jsonErrorResponse)
+  public static String parseJsonExceptionString(String jsonErrorResponse)
   {
     StringBuilder errorMessage = new StringBuilder(
             "\n============= PDB Rest Client RunTime error =============\n");
 
-    
-//    {
-//      "responseHeader":{
-//        "status":0,
-//        "QTime":0,
-//        "params":{
-//          "q":"(text:q93xj9_soltu) AND molecule_sequence:['' TO *] AND status:REL",
-//          "fl":"pdb_id,title,experimental_method,resolution",
-//          "start":"0",
-//          "sort":"overall_quality desc",
-//          "rows":"500",
-//          "wt":"json"}},
-//      "response":{"numFound":1,"start":0,"docs":[
-//          {
-//            "experimental_method":["X-ray diffraction"],
-//            "pdb_id":"4zhp",
-//            "resolution":2.46,
-//            "title":"The crystal structure of Potato ferredoxin I with 2Fe-2S cluster"}]
-//      }}
-//    
+    // {
+    // "responseHeader":{
+    // "status":0,
+    // "QTime":0,
+    // "params":{
+    // "q":"(text:q93xj9_soltu) AND molecule_sequence:['' TO *] AND status:REL",
+    // "fl":"pdb_id,title,experimental_method,resolution",
+    // "start":"0",
+    // "sort":"overall_quality desc",
+    // "rows":"500",
+    // "wt":"json"}},
+    // "response":{"numFound":1,"start":0,"docs":[
+    // {
+    // "experimental_method":["X-ray diffraction"],
+    // "pdb_id":"4zhp",
+    // "resolution":2.46,
+    // "title":"The crystal structure of Potato ferredoxin I with 2Fe-2S
+    // cluster"}]
+    // }}
+    //
     try
     {
-      Map<String, Object> jsonObj = (Map<String, Object>) JSONUtils.parse(jsonErrorResponse);
-      Map<String, Object> errorResponse = (Map<String, Object>) jsonObj.get("error");
+      Map<String, Object> jsonObj = (Map<String, Object>) JSONUtils
+              .parse(jsonErrorResponse);
+      Map<String, Object> errorResponse = (Map<String, Object>) jsonObj
+              .get("error");
 
       Map<String, Object> responseHeader = (Map<String, Object>) jsonObj
               .get("responseHeader");
-      Map<String, Object> paramsObj = (Map<String, Object>) responseHeader.get("params");
+      Map<String, Object> paramsObj = (Map<String, Object>) responseHeader
+              .get("params");
       String status = responseHeader.get("status").toString();
       String message = errorResponse.get("msg").toString();
       String query = paramsObj.get("q").toString();
@@ -311,27 +354,33 @@ public static String parseJsonExceptionString(String jsonErrorResponse)
     {
       if (jsonObj == null)
       {
-        jsonObj = (Map<String, Object>) JSONUtils.parse(pdbJsonResponseString);
+        jsonObj = (Map<String, Object>) JSONUtils
+                .parse(pdbJsonResponseString);
       }
-      Map<String, Object> pdbResponse = (Map<String, Object>) jsonObj.get("response");
-      String queryTime = ((Map<String, Object>) jsonObj.get("responseHeader"))
-              .get("QTime").toString();
+      Map<String, Object> pdbResponse = (Map<String, Object>) jsonObj
+              .get("response");
+      String queryTime = ((Map<String, Object>) jsonObj
+              .get("responseHeader")).get("QTime").toString();
       int numFound = Integer
               .valueOf(pdbResponse.get("numFound").toString());
+      List<Object> docs = (List<Object>) pdbResponse.get("docs");
+
+      result = new ArrayList<FTSData>();
       if (numFound > 0)
       {
-        result = new ArrayList<>();
-        List<Object> docs = (List<Object>) pdbResponse.get("docs");
-        for (Iterator<Object> docIter = docs.iterator(); docIter
-                .hasNext();)
+
+        for (Iterator<Object> docIter = docs.iterator(); docIter.hasNext();)
         {
           Map<String, Object> doc = (Map<String, Object>) docIter.next();
           result.add(getFTSData(doc, pdbRestRequest));
         }
-        searchResult.setNumberOfItemsFound(numFound);
-        searchResult.setResponseTime(queryTime);
-        searchResult.setSearchSummary(result);
       }
+      // this is the total number found by the query,
+      // rather than the set returned in SearchSummary
+      searchResult.setNumberOfItemsFound(numFound);
+      searchResult.setResponseTime(queryTime);
+      searchResult.setSearchSummary(result);
+
     } catch (ParseException e)
     {
       e.printStackTrace();
@@ -364,8 +413,10 @@ public static String parseJsonExceptionString(String jsonErrorResponse)
 
     for (FTSDataColumnI field : diplayFields)
     {
+      // System.out.println("Field " + field);
       String fieldData = (pdbJsonDoc.get(field.getCode()) == null) ? ""
               : pdbJsonDoc.get(field.getCode()).toString();
+      // System.out.println("Field Data : " + fieldData);
       if (field.isPrimaryKeyColumn())
       {
         primaryKey = fieldData;
@@ -469,6 +520,7 @@ public static String parseJsonExceptionString(String jsonErrorResponse)
 
   private Collection<FTSDataColumnI> allDefaultDisplayedStructureDataColumns;
 
+  @Override
   public Collection<FTSDataColumnI> getAllDefaultDisplayedStructureDataColumns()
   {
     if (allDefaultDisplayedStructureDataColumns == null
@@ -480,6 +532,26 @@ public static String parseJsonExceptionString(String jsonErrorResponse)
     }
     return allDefaultDisplayedStructureDataColumns;
   }
-  
-  
+
+  @Override
+  public String[] getPreferencesColumnsFor(PreferenceSource source)
+  {
+    String[] columnNames = null;
+    switch (source)
+    {
+    case SEARCH_SUMMARY:
+      columnNames = new String[] { "", "Display", "Group" };
+      break;
+    case STRUCTURE_CHOOSER:
+      columnNames = new String[] { "", "Display", "Group" };
+      break;
+    case PREFERENCES:
+      columnNames = new String[] { "PDB Field", "Show in search summary",
+          "Show in structure summary" };
+      break;
+    default:
+      break;
+    }
+    return columnNames;
+  }
 }
diff --git a/src/jalview/fts/service/threedbeacons/.gitignore b/src/jalview/fts/service/threedbeacons/.gitignore
new file mode 100644 (file)
index 0000000..9a003b7
--- /dev/null
@@ -0,0 +1 @@
+/tdbresttest2.java
diff --git a/src/jalview/fts/service/threedbeacons/TDB_FTSData.java b/src/jalview/fts/service/threedbeacons/TDB_FTSData.java
new file mode 100644 (file)
index 0000000..6745bb8
--- /dev/null
@@ -0,0 +1,120 @@
+package jalview.fts.service.threedbeacons;
+
+import java.util.Map;
+import java.util.Objects;
+
+import jalview.fts.api.FTSData;
+
+/**
+ * TDB result bean - holds filtered fields for GUI and essential metadata fields
+ * for back end
+ * 
+ * @author jprocter
+ *
+ */
+public class TDB_FTSData implements FTSData
+{
+  String primaryKey;
+
+  Object[] summaryRowData;
+
+  /*
+   * fields in the JSON object 
+   */
+  public static String Uniprot_Id= "id";
+  public static String Uniprot_Start= "uniprot_start";
+  public static String Uniprot_End= "uniprot_end";
+  public static String Provider= "provider";
+  public static String Model_id= "model_identifier";
+  public static String Model_Category= "model_category";
+  public static String Model_Type= "model_type";
+  public static String Model_Title="model_title";
+  public static String Resolution= "resolution";
+  public static String Confidence= "confidence_avg_local_score";
+  public static String Confidence_Score_Type= "confidence_type";
+  public static String Confidence_Score_Version= "confidence_version";
+  public static String Coverage= "coverage";
+  public static String Sequence_Identity= "sequence_identity";
+  public static String Created_Date= "created";
+  public static String UniProt_Accession= "uniprot_accession";
+  public static String Url= "model_url";
+  public static String Page_URL= "model_page_url";
+  public static String Ensemble_Sample_Url= "ensembl_sample_url";
+
+  /**
+   * original response from server
+   */
+  Map<String, Object> tdb_entry;
+
+  public TDB_FTSData(String primaryKey,
+          Map<String, Object> tdbJsonStructure, Object[] summaryData)
+  {
+    this.primaryKey = primaryKey;
+    tdb_entry = tdbJsonStructure;
+    this.summaryRowData = summaryData;
+  }
+
+  public Object getField(String key)
+  {
+    return tdb_entry.get(key);
+  }
+
+  @Override
+  public Object[] getSummaryData()
+  {
+    return summaryRowData;
+  }
+
+  @Override
+  public Object getPrimaryKey()
+  {
+    return primaryKey;
+  }
+
+  /**
+   * Returns a string representation of this object;
+   */
+  @Override
+  public String toString()
+  {
+    StringBuilder summaryFieldValues = new StringBuilder();
+    for (Object summaryField : summaryRowData)
+    {
+      summaryFieldValues
+              .append(summaryField == null ? " " : summaryField.toString())
+              .append("\t");
+    }
+    return summaryFieldValues.toString();
+  }
+
+  /**
+   * Returns hash code value for this object
+   */
+  @Override
+  public int hashCode()
+  {
+    return Objects.hash(primaryKey, this.toString());
+  }
+
+  @Override
+  public boolean equals(Object that)
+  {
+    return this.toString().equals(that.toString());
+  }
+
+  public String getProvider()
+  {
+    return (String) getField(Provider);
+  }
+
+  public String getModelViewUrl()
+  {
+    return (String) getField(Page_URL);
+  }
+
+  public String getModelId()
+  {
+    return (String) getField(Model_id);
+  }
+
+}
diff --git a/src/jalview/fts/service/threedbeacons/TDBeaconsFTSPanel.java b/src/jalview/fts/service/threedbeacons/TDBeaconsFTSPanel.java
new file mode 100644 (file)
index 0000000..7fa6aa5
--- /dev/null
@@ -0,0 +1,280 @@
+package jalview.fts.service.threedbeacons;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import javax.swing.SwingUtilities;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.core.GFTSPanel;
+import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.gui.SequenceFetcher;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatI;
+import jalview.io.FileLoader;
+import jalview.io.FormatAdapter;
+import jalview.util.MessageManager;
+
+@SuppressWarnings("serial")
+public class TDBeaconsFTSPanel extends GFTSPanel
+{
+  private static String defaultFTSFrameTitle = MessageManager
+          .getString("label.pdb_sequence_fetcher");
+
+  private static Map<String, Integer> tempUserPrefs = new HashMap<>();
+
+  private static final String THREEDB_FTS_CACHE_KEY = "CACHE.THREEDB_FTS";
+
+  private static final String THREEDB_AUTOSEARCH = "FTS.THREEDB.AUTOSEARCH";
+
+  private static HttpURLConnection connection;
+
+  public TDBeaconsFTSPanel(SequenceFetcher fetcher)
+  {
+    // no ID retrieval option for TD Beacons just now
+    super(null);
+    pageLimit = TDBeaconsFTSRestClient.getInstance()
+            .getDefaultResponsePageSize();
+    this.seqFetcher = fetcher;
+    this.progressIndicator = (fetcher == null) ? null
+            : fetcher.getProgressIndicator();
+  }
+
+  @Override
+  public void searchAction(boolean isFreshSearch)
+  {
+    mainFrame.requestFocusInWindow();
+    if (isFreshSearch)
+    {
+      offSet = 0;
+    }
+    new Thread()
+    {
+      @Override
+      public void run()
+      {
+        reset();
+        boolean allowEmptySequence = false;
+        if (getTypedText().length() > 0)
+        {
+          setSearchInProgress(true);
+          long startTime = System.currentTimeMillis();
+
+          String searchTarget = ((FTSDataColumnI) cmb_searchTarget
+                  .getSelectedItem()).getCode();
+          wantedFields = TDBeaconsFTSRestClient.getInstance()
+                  .getAllDefaultDisplayedFTSDataColumns();
+          String searchTerm = getTypedText(); // to add : decodeSearchTerm
+
+          FTSRestRequest request = new FTSRestRequest();
+          request.setAllowEmptySeq(allowEmptySequence);
+          request.setResponseSize(100);
+          // expect it to be uniprot accesssion
+          request.setSearchTerm(searchTerm + ".json");
+          request.setOffSet(offSet);
+          request.setWantedFields(wantedFields);
+          FTSRestClientI tdbRestClient = TDBeaconsFTSRestClient
+                  .getInstance();
+          FTSRestResponse resultList;
+          try
+          {
+            resultList = tdbRestClient.executeRequest(request);
+          } catch (Exception e)
+          {
+            setErrorMessage(e.getMessage());
+            checkForErrors();
+            setSearchInProgress(false);
+            return;
+          }
+
+          if (resultList.getSearchSummary() != null
+                  && resultList.getSearchSummary().size() > 0)
+          {
+            getResultTable().setModel(FTSRestResponse.getTableModel(request,
+                    resultList.getSearchSummary()));
+            FTSRestResponse.configureTableColumn(getResultTable(),
+                    wantedFields, tempUserPrefs);
+            getResultTable().setVisible(true);
+          }
+
+          long endTime = System.currentTimeMillis();
+          totalResultSetCount = resultList.getNumberOfItemsFound();
+          resultSetCount = resultList.getSearchSummary() == null ? 0
+                  : resultList.getSearchSummary().size();
+          String result = (resultSetCount > 0)
+                  ? MessageManager.getString("label.results")
+                  : MessageManager.getString("label.result");
+
+          if (isPaginationEnabled() && resultSetCount > 0)
+          {
+            String f1 = totalNumberformatter
+                    .format(Integer.valueOf(offSet + 1));
+            String f2 = totalNumberformatter
+                    .format(Integer.valueOf(offSet + resultSetCount));
+            String f3 = totalNumberformatter
+                    .format(Integer.valueOf(totalResultSetCount));
+            updateSearchFrameTitle(defaultFTSFrameTitle + " - " + result
+                    + " " + f1 + " to " + f2 + " of " + f3 + " " + " ("
+                    + (endTime - startTime) + " milli secs)");
+          }
+          else
+          {
+            updateSearchFrameTitle(defaultFTSFrameTitle + " - "
+                    + resultSetCount + " " + result + " ("
+                    + (endTime - startTime) + " milli secs)");
+          }
+
+          setSearchInProgress(false);
+          refreshPaginatorState();
+          updateSummaryTableSelections();
+        }
+        txt_search.updateCache();
+      }
+    }.start();
+  }
+
+  @Override
+  public void okAction()
+  {
+    // mainFrame.dispose();
+    disableActionButtons();
+    StringBuilder selectedIds = new StringBuilder();
+    final HashSet<String> selectedIdsSet = new HashSet<>();
+    int primaryKeyColIndex = 0;
+    try
+    {
+      primaryKeyColIndex = getFTSRestClient()
+              .getPrimaryKeyColumIndex(wantedFields, false);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    int[] selectedRows = getResultTable().getSelectedRows();
+    String searchTerm = getTypedText();
+    for (int summaryRow : selectedRows)
+    {
+      String idStr = getResultTable()
+              .getValueAt(summaryRow, primaryKeyColIndex).toString();
+      selectedIdsSet.add(idStr);
+    }
+
+    for (String idStr : paginatorCart)
+    {
+      selectedIdsSet.add(idStr);
+    }
+
+    for (String selectedId : selectedIdsSet)
+    {
+      selectedIds.append(selectedId).append(";");
+    }
+
+    SwingUtilities.invokeLater(new Runnable()
+    {
+      @Override
+      public void run()
+      {
+        AlignmentI allSeqs = null;
+        FormatAdapter fl = new jalview.io.FormatAdapter();
+        for (String tdbURL : selectedIdsSet)
+        {
+          try
+          {
+            // retrieve the structure via its URL
+            AlignmentI tdbAl = fl.readFile(tdbURL, DataSourceType.URL,
+                    FileFormat.MMCif);
+
+            // TODO: pad structure according to its Uniprot Start so all line up w.r.t. the Uniprot reference sequence
+            // TODO: give the structure a sensible name (not the giant URL *:o) )
+            if (tdbAl != null)
+            {
+              if (allSeqs != null)
+              {
+                allSeqs.append(tdbAl);
+              }
+              else
+              {
+                allSeqs = tdbAl;
+              }
+            }
+          } catch (Exception x)
+          {
+            Cache.log.warn(
+                    "Couldn't retrieve 3d-beacons model for uniprot id"
+                            + searchTerm + " : " + tdbURL,
+                    x);
+          }
+        }
+        seqFetcher.parseResult(allSeqs,
+                "3D-Beacons models for " + searchTerm, FileFormat.MMCif,
+                null);
+
+      }
+    });
+    delayAndEnableActionButtons();
+  }
+
+  @Override
+  public FTSRestClientI getFTSRestClient()
+  {
+    return TDBeaconsFTSRestClient.getInstance();
+  }
+
+  @Override
+  public String getFTSFrameTitle()
+  {
+    return defaultFTSFrameTitle;
+  }
+
+  @Override
+  public boolean isPaginationEnabled()
+  {
+    return true;
+  }
+
+  @Override
+  public Map<String, Integer> getTempUserPrefs()
+  {
+    return tempUserPrefs;
+  }
+
+  @Override
+  public String getCacheKey()
+  {
+    return THREEDB_FTS_CACHE_KEY;
+  }
+
+  @Override
+  public String getAutosearchPreference()
+  {
+    return THREEDB_AUTOSEARCH;
+  }
+
+  @Override
+  protected void showHelp()
+  {
+    System.out.println("No help implemented yet.");
+
+  }
+
+  public static String decodeSearchTerm(String enteredText)
+  {
+    // no multiple query support yet
+    return enteredText;
+  }
+}
diff --git a/src/jalview/fts/service/threedbeacons/TDBeaconsFTSRestClient.java b/src/jalview/fts/service/threedbeacons/TDBeaconsFTSRestClient.java
new file mode 100644 (file)
index 0000000..ccdc525
--- /dev/null
@@ -0,0 +1,350 @@
+package jalview.fts.service.threedbeacons;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.ws.rs.core.MediaType;
+
+import org.json.simple.parser.ParseException;
+
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.api.StructureFTSRestClientI;
+import jalview.fts.core.FTSRestClient;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.util.JSONUtils;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+
+public class TDBeaconsFTSRestClient extends FTSRestClient
+        implements StructureFTSRestClientI
+{
+  /**
+   * production server URI
+   */
+  private static String TDB_PROD_API="https://www.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/api/uniprot/summary/";
+  /**
+   * dev server URI
+   */
+  private static String TDB_DEV_API="https://wwwdev.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/api/uniprot/summary/";
+  private static String DEFAULT_THREEDBEACONS_DOMAIN = TDB_PROD_API; 
+
+  public static FTSRestClientI instance = null;
+
+  protected TDBeaconsFTSRestClient()
+  {
+  }
+  @SuppressWarnings("unchecked")
+  @Override
+  public FTSRestResponse executeRequest(FTSRestRequest tdbRestRequest)
+          throws Exception
+  {
+    try
+    {
+      String query = tdbRestRequest.getSearchTerm();
+      Client client;
+      Class<ClientResponse> clientResponseClass;
+      if (Platform.isJS())
+      {
+        // JavaScript only
+        client = (Client) (Object) new jalview.javascript.web.Client();
+        clientResponseClass = (Class<ClientResponse>) (Object) jalview.javascript.web.ClientResponse.class;
+      }
+      else
+      /**
+       * Java only
+       * 
+       * @j2sIgnore
+       */
+      {
+        client = Client.create(new DefaultClientConfig());
+        clientResponseClass = ClientResponse.class;
+      }
+
+      WebResource webResource;
+      webResource = client.resource(DEFAULT_THREEDBEACONS_DOMAIN+query);
+
+      URI uri = webResource.getURI();
+      System.out.println(uri.toString());
+
+      // Execute the REST request
+      ClientResponse clientResponse;
+      if (isMocked()) { 
+        clientResponse = null;
+      }
+      else
+      {
+        clientResponse = webResource.accept(MediaType.APPLICATION_JSON)
+                .get(clientResponseClass);
+      }
+
+      // Get the JSON string from the response object or directly from the
+      // client (JavaScript)
+      Map<String, Object> jsonObj = null;
+      String responseString = null;
+
+      // Check the response status and report exception if one occurs
+      int responseStatus = isMocked() ? (mockQueries.containsKey(query) ? 200 : 404) : clientResponse.getStatus();
+      switch (responseStatus)
+      {
+      // if success
+      case 200:
+        if (Platform.isJS())
+        {
+          jsonObj = clientResponse.getEntity(Map.class);
+        }
+        else
+        {
+          responseString = isMocked() ? mockQueries.get(query): clientResponse.getEntity(String.class);
+        }
+        break;
+      case 400:
+        throw new Exception(parseJsonExceptionString(responseString));
+      case 404:
+        return emptyTDBeaconsJsonResponse();
+      default:
+        throw new Exception(
+                getMessageByHTTPStatusCode(responseStatus, "3DBeacons"));
+      }
+      // Process the response and return the result to the caller.
+      return parseTDBeaconsJsonResponse(responseString, jsonObj,
+              tdbRestRequest);
+    } catch (Exception e)
+    {
+      String exceptionMsg = e.getMessage();
+      if (exceptionMsg != null)
+      {
+        if (exceptionMsg.contains("SocketException"))
+        {
+          // No internet connection
+          throw new Exception(MessageManager.getString(
+                  "exception.unable_to_detect_internet_connection"));
+        }
+        else if (exceptionMsg.contains("UnknownHostException"))
+        {
+          // The server is unreachable
+          throw new Exception(MessageManager.formatMessage(
+                  "exception.fts_server_unreachable", "3DB Hub"));
+        }
+      }
+      throw e;
+      
+    }
+
+  }
+
+  /**
+   * returns response for when the 3D-Beacons service doesn't have a record for
+   * the given query - in 2.11.2 this triggers a failover to the PDBe FTS 
+   * 
+   * @return null
+   */
+  private FTSRestResponse emptyTDBeaconsJsonResponse()
+  {
+    return null;
+  }
+
+  public String setSearchTerm(String term)
+  {
+    return term;
+  }
+
+  public static FTSRestResponse parseTDBeaconsJsonResponse(
+          String tdbJsonResponseString, FTSRestRequest tdbRestRequest)
+  {
+    return parseTDBeaconsJsonResponse(tdbJsonResponseString,
+            (Map<String, Object>) null, tdbRestRequest);
+  }
+
+  @SuppressWarnings("unchecked")
+  public static FTSRestResponse parseTDBeaconsJsonResponse(
+          String tdbJsonResponseString, Map<String, Object> jsonObj,
+          FTSRestRequest tdbRestRequest)
+  {
+    FTSRestResponse searchResult = new FTSRestResponse();
+    List<FTSData> result = null;
+
+    try
+    {
+      if (jsonObj == null)
+      {
+        jsonObj = (Map<String, Object>) JSONUtils
+                .parse(tdbJsonResponseString);
+      }
+
+      Object uniprot_entry = jsonObj.get("uniprot_entry");
+      // TODO: decide if anything from uniprot_entry needs to be reported via
+      // the FTSRestResponse object
+      // Arnaud added seqLength = (Long) ((Map<String, Object>)
+      // jsonObj.get("uniprot_entry")).get("sequence_length");
+
+      List<Object> structures = (List<Object>) jsonObj.get("structures");
+      result = new ArrayList<>();
+
+      int numFound = 0;
+      for (Iterator<Object> strucIter = structures.iterator(); strucIter
+              .hasNext();)
+      {
+        Map<String, Object> structure = (Map<String, Object>) strucIter
+                .next();
+        result.add(getFTSData(structure, tdbRestRequest));
+        numFound++;
+      }
+
+      searchResult.setNumberOfItemsFound(numFound);
+      searchResult.setSearchSummary(result);
+
+    } catch (ParseException e)
+    {
+      e.printStackTrace();
+    }
+    return searchResult;
+  }
+
+  private static FTSData getFTSData(Map<String, Object> tdbJsonStructure,
+          FTSRestRequest tdbRequest)
+  {
+    String primaryKey = null;
+    Object[] summaryRowData;
+
+    SequenceI associatedSequence;
+
+    Collection<FTSDataColumnI> displayFields = tdbRequest.getWantedFields();
+    SequenceI associatedSeq = tdbRequest.getAssociatedSequence();
+    int colCounter = 0;
+    summaryRowData = new Object[(associatedSeq != null)
+                                ? displayFields.size() + 1
+                                : displayFields.size()];
+                        if (associatedSeq != null)
+                        {
+                          associatedSequence = associatedSeq;
+                          summaryRowData[0] = associatedSequence;
+                          colCounter = 1;
+                        }
+
+    for (FTSDataColumnI field : displayFields)
+    {
+      String fieldData = (tdbJsonStructure.get(field.getCode()) == null)
+              ? " "
+              : tdbJsonStructure.get(field.getCode()).toString();
+      // System.out.println("Field : " + field + " Data : " + fieldData);
+      if (field.isPrimaryKeyColumn())
+      {
+        primaryKey = fieldData;
+        summaryRowData[colCounter++] = primaryKey;
+      }
+      else if (fieldData == null || fieldData.trim().isEmpty())
+      {
+        summaryRowData[colCounter++] = null;
+      }
+      else
+      {
+        try
+        {
+          summaryRowData[colCounter++] = (field.getDataType()
+                  .getDataTypeClass() == Integer.class)
+                          ? Integer.valueOf(fieldData)
+                          : (field.getDataType()
+                                  .getDataTypeClass() == Double.class)
+                                          ? Double.valueOf(fieldData)
+                                          : fieldData;
+        } catch (Exception e)
+        {
+          // e.printStackTrace();
+          System.out.println("offending value:" + fieldData + fieldData);
+        }
+      }
+    }
+    final String primaryKey1 = primaryKey;
+    final Object[] summaryRowData1 = summaryRowData;
+
+    return new TDB_FTSData(primaryKey, tdbJsonStructure, summaryRowData1);
+  }
+
+  // private static FTSData getFTSData(Map<String, Object> doc,
+  // FTSRestRequest tdbRestRequest)
+  // {
+  // String primaryKey = null;
+  //
+  // Object[] summaryRowData;
+  //
+  // Collection<FTSDataColumnI> displayFields =
+  // tdbRestRequest.getWantedFields();
+  // int colCounter = 0;
+  // summaryRowData = new Object[displayFields.size() + 1];
+  //
+  // return null;
+  // }
+
+  private String parseJsonExceptionString(String jsonErrorString)
+  {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  @Override
+  public String getColumnDataConfigFileName()
+  {
+    return "/fts/tdbeacons_data_columns.txt";
+  }
+
+  public static FTSRestClientI getInstance()
+  {
+    if (instance == null)
+    {
+      instance = new TDBeaconsFTSRestClient();
+    }
+    return instance;
+  }
+
+  private Collection<FTSDataColumnI> allDefaultDisplayedStructureDataColumns;
+
+  public Collection<FTSDataColumnI> getAllDefaultDisplayedStructureDataColumns()
+  {
+    if (allDefaultDisplayedStructureDataColumns == null
+            || allDefaultDisplayedStructureDataColumns.isEmpty())
+    {
+      allDefaultDisplayedStructureDataColumns = new ArrayList<>();
+      allDefaultDisplayedStructureDataColumns
+              .addAll(super.getAllDefaultDisplayedFTSDataColumns());
+    }
+    return allDefaultDisplayedStructureDataColumns;
+  }
+
+  @Override
+  public String[] getPreferencesColumnsFor(PreferenceSource source)
+  {
+    String[] columnNames = null;
+    switch (source)
+    {
+    case SEARCH_SUMMARY:
+      columnNames = new String[] { "", "Display", "Group" };
+      break;
+    case STRUCTURE_CHOOSER:
+      columnNames = new String[] { "", "Display", "Group" };
+      break;
+    case PREFERENCES:
+      columnNames = new String[] { "3DB Beacons Field", "Show in search summary",
+          "Show in structure summary" };
+      break;
+    default:
+      break;
+    }
+    return columnNames;
+  }
+}
index 5370437..8deff01 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -484,9 +486,9 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                 }
               }
             });
-    if (Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase()
+    if (Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase(Locale.ROOT)
             .indexOf("devel") > -1
-            || Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase()
+            || Cache.getDefault("VERSION", "DEVELOPMENT").toLowerCase(Locale.ROOT)
                     .indexOf("test") > -1)
     {
       formatMenu.add(vsel);
@@ -565,7 +567,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().moveCursor(0, 1);
+            alignPanel.getSeqPanel().moveCursor(0, 1, evt.isShiftDown());
           }
           break;
 
@@ -576,7 +578,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           if (viewport.cursorMode)
           {
-            alignPanel.getSeqPanel().moveCursor(0, -1);
+            alignPanel.getSeqPanel().moveCursor(0, -1,evt.isShiftDown());
           }
 
           break;
@@ -589,7 +591,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.getSeqPanel().moveCursor(-1, 0);
+            alignPanel.getSeqPanel().moveCursor(-1, 0, evt.isShiftDown());
           }
 
           break;
@@ -601,7 +603,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
           }
           else
           {
-            alignPanel.getSeqPanel().moveCursor(1, 0);
+            alignPanel.getSeqPanel().moveCursor(1, 0, evt.isShiftDown());
           }
           break;
 
@@ -2825,7 +2827,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
   @Override
   public void findMenuItem_actionPerformed(ActionEvent e)
   {
-    new Finder(alignPanel);
+    new Finder(alignPanel, false, null);
   }
 
   /**
@@ -4297,7 +4299,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener,
                       jws2servs.attachWSMenuEntry(webService, me);
                       for (Jws2Instance sv : jws2servs.getServices())
                       {
-                        if (sv.description.toLowerCase().contains("jpred"))
+                        if (sv.description.toLowerCase(Locale.ROOT).contains("jpred"))
                         {
                           for (JMenuItem jmi : legacyItems)
                           {
index 0125f0d..55abea6 100644 (file)
@@ -1022,8 +1022,8 @@ public class AlignViewport extends AlignmentViewport
     
     FeatureRenderer fr = getAlignPanel().getSeqPanel().seqCanvas
             .getFeatureRenderer();
-    List<String> origRenderOrder = new ArrayList(),
-            origGroups = new ArrayList();
+    List<String> origRenderOrder = new ArrayList<>();
+    List<String> origGroups = new ArrayList<>();
     // preserve original render order - allows differentiation between user configured colours and autogenerated ones
     origRenderOrder.addAll(fr.getRenderOrder());
     origGroups.addAll(fr.getFeatureGroups());
@@ -1034,7 +1034,7 @@ public class AlignViewport extends AlignmentViewport
     if (!mergeOnly)
     {
       // only clear displayed features if we are mergeing
-      displayed.clear();
+      // displayed.clear();
     }
     // TODO this clears displayed.featuresRegistered - do we care?
     //
@@ -1066,6 +1066,10 @@ public class AlignViewport extends AlignmentViewport
         {
           displayed.setVisible(type);
         }
+        else if (featureSettings.isFeatureHidden(type))
+        {
+          displayed.setHidden(type);
+        }
       }
     }
 
@@ -1094,6 +1098,8 @@ public class AlignViewport extends AlignmentViewport
       fr.orderFeatures(featureSettings);
     }
     fr.setTransparency(featureSettings.getTransparency());
+
+    fr.notifyFeaturesChanged();
   }
 
   public String getViewName()
index d84287f..568ca47 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import jalview.api.FeatureRenderer;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentAnnotation;
@@ -337,7 +339,7 @@ public class AnnotationExporter extends JPanel
 
     boolean nucleotide = ap.av.isNucleotide();
     String complement = nucleotide
-            ? MessageManager.getString("label.protein").toLowerCase()
+            ? MessageManager.getString("label.protein").toLowerCase(Locale.ROOT)
             : "CDS";
     JLabel label = new JLabel(
             MessageManager.formatMessage("label.include_linked_features",
index 5a681f1..21c45e9 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import jalview.analysis.AlignSeq;
 import jalview.analysis.AlignmentUtils;
 import jalview.datamodel.Alignment;
@@ -754,12 +756,12 @@ public class AnnotationLabels extends JPanel
       // jalview.gui.SeqPanel.mouseMoved(..) that formats sequence feature
       // tooltips
       String desc = aa.getDescription(true).trim();
-      if (!desc.toLowerCase().startsWith(HTML_START_TAG))
+      if (!desc.toLowerCase(Locale.ROOT).startsWith(HTML_START_TAG))
       {
         tooltip.append(HTML_START_TAG);
         desc = desc.replace("<", "&lt;");
       }
-      else if (desc.toLowerCase().endsWith(HTML_END_TAG))
+      else if (desc.toLowerCase(Locale.ROOT).endsWith(HTML_END_TAG))
       {
         desc = desc.substring(0, desc.length() - HTML_END_TAG.length());
       }
index 7cf10e7..217d2b1 100644 (file)
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Graphics;
 import java.io.File;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -38,11 +39,11 @@ import javax.swing.event.InternalFrameEvent;
 
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
-import jalview.datamodel.AlignmentI;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.datamodel.StructureViewerModel;
 import jalview.datamodel.StructureViewerModel.StructureData;
+import jalview.fts.service.alphafold.AlphafoldRestClient;
 import jalview.gui.ImageExporter.ImageWriterI;
 import jalview.gui.StructureViewer.ViewerType;
 import jalview.structure.StructureCommand;
@@ -51,7 +52,6 @@ import jalview.util.BrowserLauncher;
 import jalview.util.ImageMaker;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
-import jalview.ws.dbsources.Pdb;
 
 public class AppJmol extends StructureViewerBase
 {
@@ -271,7 +271,7 @@ public class AppJmol extends StructureViewerBase
     _started = true;
     try
     {
-      List<String> files = fetchPdbFiles();
+      List<String> files = jmb.fetchPdbFiles(this);
       if (files.size() > 0)
       {
         showFilesInViewer(files);
@@ -417,110 +417,6 @@ public class AppJmol extends StructureViewerBase
   }
 
   /**
-   * Retrieves and saves as file any modelled PDB entries for which we do not
-   * already have a file saved. Returns a list of absolute paths to structure
-   * files which were either retrieved, or already stored but not modelled in
-   * the structure viewer (i.e. files to add to the viewer display).
-   * 
-   * @return
-   */
-  List<String> fetchPdbFiles()
-  {
-    // todo - record which pdbids were successfully imported.
-    StringBuilder errormsgs = new StringBuilder();
-
-    List<String> files = new ArrayList<>();
-    String pdbid = "";
-    try
-    {
-      String[] filesInViewer = jmb.getStructureFiles();
-      // TODO: replace with reference fetching/transfer code (validate PDBentry
-      // as a DBRef?)
-      Pdb pdbclient = new Pdb();
-      for (int pi = 0; pi < jmb.getPdbCount(); pi++)
-      {
-        String file = jmb.getPdbEntry(pi).getFile();
-        if (file == null)
-        {
-          // todo: extract block as method and pull up (also ChimeraViewFrame)
-          // retrieve the pdb and store it locally
-          AlignmentI pdbseq = null;
-          pdbid = jmb.getPdbEntry(pi).getId();
-          long hdl = pdbid.hashCode() - System.currentTimeMillis();
-          setProgressMessage(MessageManager
-                  .formatMessage("status.fetching_pdb", new String[]
-                  { pdbid }), hdl);
-          try
-          {
-            pdbseq = pdbclient.getSequenceRecords(pdbid);
-          } catch (OutOfMemoryError oomerror)
-          {
-            new OOMWarning("Retrieving PDB id " + pdbid, oomerror);
-          } catch (Exception ex)
-          {
-            ex.printStackTrace();
-            errormsgs.append("'").append(pdbid).append("'");
-          } finally
-          {
-            setProgressMessage(
-                    MessageManager.getString("label.state_completed"), hdl);
-          }
-          if (pdbseq != null)
-          {
-            // just transfer the file name from the first sequence's first
-            // PDBEntry
-            file = new File(pdbseq.getSequenceAt(0).getAllPDBEntries()
-                    .elementAt(0).getFile()).getAbsolutePath();
-            jmb.getPdbEntry(pi).setFile(file);
-            files.add(file);
-          }
-          else
-          {
-            errormsgs.append("'").append(pdbid).append("' ");
-          }
-        }
-        else
-        {
-          if (filesInViewer != null && filesInViewer.length > 0)
-          {
-            addingStructures = true; // already files loaded.
-            for (int c = 0; c < filesInViewer.length; c++)
-            {
-              if (Platform.pathEquals(filesInViewer[c], file))
-              {
-                file = null;
-                break;
-              }
-            }
-          }
-          if (file != null)
-          {
-            files.add(file);
-          }
-        }
-      }
-    } catch (OutOfMemoryError oomerror)
-    {
-      new OOMWarning("Retrieving PDB files: " + pdbid, oomerror);
-    } catch (Exception ex)
-    {
-      ex.printStackTrace();
-      errormsgs.append("When retrieving pdbfiles : current was: '")
-              .append(pdbid).append("'");
-    }
-    if (errormsgs.length() > 0)
-    {
-      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-              MessageManager.formatMessage(
-                      "label.pdb_entries_couldnt_be_retrieved", new String[]
-                      { errormsgs.toString() }),
-              MessageManager.getString("label.couldnt_load_file"),
-              JvOptionPane.ERROR_MESSAGE);
-    }
-    return files;
-  }
-
-  /**
    * Outputs the Jmol viewer image as an image file, after prompting the user to
    * choose a file and (for EPS) choice of Text or Lineart character rendering
    * (unless a preference for this is set)
@@ -540,7 +436,7 @@ public class AppJmol extends StructureViewerBase
         jmb.jmolViewer.renderScreenImage(g, width, height);
       }
     };
-    String view = MessageManager.getString("action.view").toLowerCase();
+    String view = MessageManager.getString("action.view").toLowerCase(Locale.ROOT);
     ImageExporter exporter = new ImageExporter(writer,
             getProgressIndicator(), type, getTitle());
     exporter.doExport(null, this, width, height, view);
index 98787cb..037b97e 100644 (file)
@@ -22,6 +22,7 @@ package jalview.gui;
 
 import java.awt.Container;
 import java.io.File;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -33,12 +34,17 @@ import org.openscience.jmol.app.jmolpanel.console.AppConsole;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.ext.jmol.JalviewJmolBinding;
 import jalview.io.DataSourceType;
 import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
 import jalview.util.Platform;
+import jalview.ws.dbsources.EBIAlfaFold;
+import jalview.ws.dbsources.Pdb;
+import jalview.ws.utils.UrlDownloadClient;
 import javajs.util.BS;
 
 public class AppJmolBinding extends JalviewJmolBinding
@@ -189,4 +195,137 @@ public class AppJmolBinding extends JalviewJmolBinding
       Platform.cacheFileData(f);
     }
   }
+
+  /**
+   * Retrieves and saves as file any modelled PDB entries for which we do not
+   * already have a file saved. Returns a list of absolute paths to structure
+   * files which were either retrieved, or already stored but not modelled in
+   * the structure viewer (i.e. files to add to the viewer display).
+   * 
+   * Currently only used by Jmol - similar but different code used for Chimera/X
+   * and Pymol so still need to refactor
+   * 
+   * @param structureViewer
+   *          UI proxy for the structure viewer
+   * @return list of absolute paths to structures retrieved that need to be
+   *         added to the display
+   */
+  public List<String> fetchPdbFiles(StructureViewerBase structureViewer)
+  {
+    // todo - record which pdbids were successfully imported.
+    StringBuilder errormsgs = new StringBuilder();
+  
+    List<String> files = new ArrayList<>();
+    String pdbid = "";
+    try
+    {
+      String[] filesInViewer = getStructureFiles();
+      // TODO: replace with reference fetching/transfer code (validate PDBentry
+      // as a DBRef?)
+      Pdb pdbclient = new Pdb();
+      EBIAlfaFold afclient = new EBIAlfaFold();
+      
+      for (int pi = 0; pi < getPdbCount(); pi++)
+      {
+        String file = getPdbEntry(pi).getFile();
+        if (file == null)
+        {
+          // todo: extract block as method and pull up (also ChimeraViewFrame)
+          // retrieve the pdb and store it locally
+          AlignmentI pdbseq = null;
+          PDBEntry strucEntry = getPdbEntry(pi);
+          pdbid = strucEntry.getId();
+          long hdl = pdbid.hashCode() - System.currentTimeMillis();
+          structureViewer.setProgressMessage(MessageManager
+                  .formatMessage("status.fetching_pdb", new String[]
+                  { pdbid }), hdl);
+          try
+          {
+            if (afclient.isValidReference(pdbid))
+            {
+              pdbseq = afclient.getSequenceRecords(pdbid);
+            } else {
+              if (strucEntry.hasRetrievalUrl())
+              {
+                File tmpFile = File.createTempFile(pdbid, ".cif");
+                String fromUrl = strucEntry.getRetrievalUrl();
+                UrlDownloadClient.download(fromUrl, tmpFile);
+                
+                // may not need this check ?
+                file = tmpFile.getAbsolutePath();
+                if (file != null)
+                {
+                  pdbseq = EBIAlfaFold.importDownloadedStructureFromUrl(fromUrl,tmpFile,pdbid,null,null,null);
+                }
+              } else {
+                pdbseq = pdbclient.getSequenceRecords(pdbid);
+              }
+            }
+          } catch (OutOfMemoryError oomerror)
+          {
+            new OOMWarning("Retrieving PDB id " + pdbid, oomerror);
+          } catch (Exception ex)
+          {
+            ex.printStackTrace();
+            errormsgs.append("'").append(pdbid).append("'");
+          } finally
+          {
+            structureViewer.setProgressMessage(
+                    MessageManager.getString("label.state_completed"), hdl);
+          }
+          if (pdbseq != null)
+          {
+            // just transfer the file name from the first sequence's first
+            // PDBEntry
+            file = new File(pdbseq.getSequenceAt(0).getAllPDBEntries()
+                    .elementAt(0).getFile()).getAbsolutePath();
+            getPdbEntry(pi).setFile(file);
+            files.add(file);
+          }
+          else
+          {
+            errormsgs.append("'").append(pdbid).append("' ");
+          }
+        }
+        else
+        {
+          if (filesInViewer != null && filesInViewer.length > 0)
+          {
+            structureViewer.setAddingStructures(true); // already files loaded.
+            for (int c = 0; c < filesInViewer.length; c++)
+            {
+              if (Platform.pathEquals(filesInViewer[c], file))
+              {
+                file = null;
+                break;
+              }
+            }
+          }
+          if (file != null)
+          {
+            files.add(file);
+          }
+        }
+      }
+    } catch (OutOfMemoryError oomerror)
+    {
+      new OOMWarning("Retrieving PDB files: " + pdbid, oomerror);
+    } catch (Exception ex)
+    {
+      ex.printStackTrace();
+      errormsgs.append("When retrieving pdbfiles : current was: '")
+              .append(pdbid).append("'");
+    }
+    if (errormsgs.length() > 0)
+    {
+      JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+              MessageManager.formatMessage(
+                      "label.pdb_entries_couldnt_be_retrieved", new String[]
+                      { errormsgs.toString() }),
+              MessageManager.getString("label.couldnt_load_file"),
+              JvOptionPane.ERROR_MESSAGE);
+    }
+    return files;
+  }
+
 }
index 757bb01..8ba3dce 100644 (file)
@@ -256,7 +256,7 @@ public class BlogReader extends JPanel
     chan.setURL(
             jalview.bin.Cache.getDefault("JALVIEW_NEWS_RSS",
                     jalview.bin.Cache.getDefault("www.jalview.org",
-                            "http://www.jalview.org")
+                            "https://www.jalview.org")
                             + "/feeds/desktop/rss"));
     loadLastM();
     _channelModel.addChannel(chan);
index 810f40d..e3c65da 100644 (file)
@@ -491,7 +491,8 @@ public class ChimeraViewFrame extends StructureViewerBase
 
       /*
        * ensure that any newly discovered features (e.g. RESNUM)
-       * are added to any open feature settings dialog
+       * are notified to the FeatureRenderer (and added to any 
+       * open feature settings dialog)
        */
       FeatureRenderer fr = getBinding().getFeatureRenderer(null);
       if (fr != null)
index d328a0d..3f64ff1 100644 (file)
@@ -274,11 +274,8 @@ public class CutAndPasteTransfer extends GCutAndPasteTransfer
        */
       if (viewport != null && viewport.getAlignment() != null)
       {
-        if (proxyColourScheme != null)
-        {
-          viewport.applyFeaturesStyle(proxyColourScheme);
-        }
         ((AlignViewport) viewport).addAlignment(al, title);
+        viewport.applyFeaturesStyle(proxyColourScheme);
       }
       else
       {
index 39c68a0..666fb4e 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
@@ -54,8 +56,10 @@ import java.beans.PropertyChangeListener;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.lang.reflect.Field;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
@@ -134,36 +138,26 @@ import jalview.ws.utils.UrlDownloadClient;
  * @version $Revision: 1.155 $
  */
 public class Desktop extends jalview.jbgui.GDesktop
-        implements DropTargetListener, ClipboardOwner, IProgressIndicator,
-        jalview.api.StructureSelectionManagerProvider
-{
+    implements DropTargetListener, ClipboardOwner, IProgressIndicator, jalview.api.StructureSelectionManagerProvider {
   private static final String CITATION;
-  static
-  {
-    URL bg_logo_url = ChannelProperties.getImageURL(
-            "bg_logo." + String.valueOf(SplashScreen.logoSize));
-    URL uod_logo_url = ChannelProperties.getImageURL(
-            "uod_banner." + String.valueOf(SplashScreen.logoSize));
+  static {
+    URL bg_logo_url = ChannelProperties.getImageURL("bg_logo." + String.valueOf(SplashScreen.logoSize));
+    URL uod_logo_url = ChannelProperties.getImageURL("uod_banner." + String.valueOf(SplashScreen.logoSize));
     boolean logo = (bg_logo_url != null || uod_logo_url != null);
     StringBuilder sb = new StringBuilder();
-    sb.append(
-            "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.");
-    if (logo)
-    {
+    sb.append("<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.");
+    if (logo) {
       sb.append("<br>");
     }
-    sb.append(bg_logo_url == null ? ""
-            : "<img alt=\"Barton Group logo\" src=\""
-                    + bg_logo_url.toString() + "\">");
+    sb.append(bg_logo_url == null ? "" : "<img alt=\"Barton Group logo\" src=\"" + bg_logo_url.toString() + "\">");
     sb.append(uod_logo_url == null ? ""
-            : "&nbsp;<img alt=\"University of Dundee shield\" src=\""
-                    + uod_logo_url.toString() + "\">");
+        : "&nbsp;<img alt=\"University of Dundee shield\" src=\"" + uod_logo_url.toString() + "\">");
     sb.append(
-            "<br><br>For help, see the FAQ at <a href=\"https://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list");
+        "<br><br>For help, see the FAQ at <a href=\"https://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list");
     sb.append("<br><br>If  you use Jalview, please cite:"
-            + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
-            + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
-            + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033");
+        + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
+        + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
+        + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033");
     CITATION = sb.toString();
   }
 
@@ -198,9 +192,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    * @param listener
    * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
    */
-  public void addJalviewPropertyChangeListener(
-          PropertyChangeListener listener)
-  {
+  public void addJalviewPropertyChangeListener(PropertyChangeListener listener) {
     changeSupport.addJalviewPropertyChangeListener(listener);
   }
 
@@ -210,9 +202,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
    *      java.beans.PropertyChangeListener)
    */
-  public void addJalviewPropertyChangeListener(String propertyName,
-          PropertyChangeListener listener)
-  {
+  public void addJalviewPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
     changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
   }
 
@@ -222,11 +212,8 @@ public class Desktop extends jalview.jbgui.GDesktop
    * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
    *      java.beans.PropertyChangeListener)
    */
-  public void removeJalviewPropertyChangeListener(String propertyName,
-          PropertyChangeListener listener)
-  {
-    changeSupport.removeJalviewPropertyChangeListener(propertyName,
-            listener);
+  public void removeJalviewPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+    changeSupport.removeJalviewPropertyChangeListener(propertyName, listener);
   }
 
   /** Singleton Desktop instance */
@@ -234,8 +221,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   public static MyDesktopPane desktop;
 
-  public static MyDesktopPane getDesktop()
-  {
+  public static MyDesktopPane getDesktop() {
     // BH 2018 could use currentThread() here as a reference to a
     // Hashtable<Thread, MyDesktopPane> in JavaScript
     return desktop;
@@ -255,122 +241,99 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   static int fileLoadingCount = 0;
 
-  class MyDesktopManager implements DesktopManager
-  {
+  class MyDesktopManager implements DesktopManager {
 
     private DesktopManager delegate;
 
-    public MyDesktopManager(DesktopManager delegate)
-    {
+    public MyDesktopManager(DesktopManager delegate) {
       this.delegate = delegate;
     }
 
     @Override
-    public void activateFrame(JInternalFrame f)
-    {
-      try
-      {
+    public void activateFrame(JInternalFrame f) {
+      try {
         delegate.activateFrame(f);
-      } catch (NullPointerException npe)
-      {
+      } catch (NullPointerException npe) {
         Point p = getMousePosition();
         instance.showPasteMenu(p.x, p.y);
       }
     }
 
     @Override
-    public void beginDraggingFrame(JComponent f)
-    {
+    public void beginDraggingFrame(JComponent f) {
       delegate.beginDraggingFrame(f);
     }
 
     @Override
-    public void beginResizingFrame(JComponent f, int direction)
-    {
+    public void beginResizingFrame(JComponent f, int direction) {
       delegate.beginResizingFrame(f, direction);
     }
 
     @Override
-    public void closeFrame(JInternalFrame f)
-    {
+    public void closeFrame(JInternalFrame f) {
       delegate.closeFrame(f);
     }
 
     @Override
-    public void deactivateFrame(JInternalFrame f)
-    {
+    public void deactivateFrame(JInternalFrame f) {
       delegate.deactivateFrame(f);
     }
 
     @Override
-    public void deiconifyFrame(JInternalFrame f)
-    {
+    public void deiconifyFrame(JInternalFrame f) {
       delegate.deiconifyFrame(f);
     }
 
     @Override
-    public void dragFrame(JComponent f, int newX, int newY)
-    {
-      if (newY < 0)
-      {
+    public void dragFrame(JComponent f, int newX, int newY) {
+      if (newY < 0) {
         newY = 0;
       }
       delegate.dragFrame(f, newX, newY);
     }
 
     @Override
-    public void endDraggingFrame(JComponent f)
-    {
+    public void endDraggingFrame(JComponent f) {
       delegate.endDraggingFrame(f);
       desktop.repaint();
     }
 
     @Override
-    public void endResizingFrame(JComponent f)
-    {
+    public void endResizingFrame(JComponent f) {
       delegate.endResizingFrame(f);
       desktop.repaint();
     }
 
     @Override
-    public void iconifyFrame(JInternalFrame f)
-    {
+    public void iconifyFrame(JInternalFrame f) {
       delegate.iconifyFrame(f);
     }
 
     @Override
-    public void maximizeFrame(JInternalFrame f)
-    {
+    public void maximizeFrame(JInternalFrame f) {
       delegate.maximizeFrame(f);
     }
 
     @Override
-    public void minimizeFrame(JInternalFrame f)
-    {
+    public void minimizeFrame(JInternalFrame f) {
       delegate.minimizeFrame(f);
     }
 
     @Override
-    public void openFrame(JInternalFrame f)
-    {
+    public void openFrame(JInternalFrame f) {
       delegate.openFrame(f);
     }
 
     @Override
-    public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
-            int newHeight)
-    {
-      if (newY < 0)
-      {
+    public void resizeFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
+      if (newY < 0) {
         newY = 0;
       }
       delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
     }
 
     @Override
-    public void setBoundsForFrame(JComponent f, int newX, int newY,
-            int newWidth, int newHeight)
-    {
+    public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
       delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
     }
 
@@ -381,46 +344,72 @@ public class Desktop extends jalview.jbgui.GDesktop
   /**
    * Creates a new Desktop object.
    */
-  public Desktop()
-  {
+  public Desktop() {
     super();
     /**
-     * A note to implementors. It is ESSENTIAL that any activities that might
-     * block are spawned off as threads rather than waited for during this
-     * constructor.
+     * A note to implementors. It is ESSENTIAL that any activities that might block
+     * are spawned off as threads rather than waited for during this constructor.
      */
     instance = this;
 
     doConfigureStructurePrefs();
-    setTitle(ChannelProperties.getProperty("app_name") + " "
-            + Cache.getProperty("VERSION"));
-    /*
-    if (!Platform.isAMac())
-    {
-      // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
-    }
-    else
-    {
-     this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+    setTitle(ChannelProperties.getProperty("app_name") + " " + Cache.getProperty("VERSION"));
+
+    /**
+     * Set taskbar "grouped windows" name for linux desktops (works in GNOME and
+     * KDE). This uses sun.awt.X11.XToolkit.awtAppClassName which is not officially
+     * documented or guaranteed to exist, so we access it via reflection. There
+     * appear to be unfathomable criteria about what this string can contain, and it
+     * if doesn't meet those criteria then "java" (KDE) or "jalview-bin-Jalview"
+     * (GNOME) is used. "Jalview", "Jalview Develop" and "Jalview Test" seem okay,
+     * but "Jalview non-release" does not. The reflection access may generate a
+     * warning: WARNING: An illegal reflective access operation has occurred
+     * WARNING: Illegal reflective access by jalview.gui.Desktop () to field
+     * sun.awt.X11.XToolkit.awtAppClassName which I don't think can be avoided.
+     */
+    if (Platform.isLinux()) {
+      try {
+        Toolkit xToolkit = Toolkit.getDefaultToolkit();
+        Field[] declaredFields = xToolkit.getClass().getDeclaredFields();
+        Field awtAppClassNameField = null;
+
+        if (Arrays.stream(declaredFields).anyMatch(f -> f.getName().equals("awtAppClassName"))) {
+          awtAppClassNameField = xToolkit.getClass().getDeclaredField("awtAppClassName");
+        }
+
+        String title = ChannelProperties.getProperty("app_name");
+        if (awtAppClassNameField != null) {
+          awtAppClassNameField.setAccessible(true);
+          awtAppClassNameField.set(xToolkit, title);
+        } else {
+          Cache.log.debug("XToolkit: awtAppClassName not found");
+        }
+      } catch (Exception e) {
+        Cache.debug("Error setting awtAppClassName");
+        Cache.trace(Cache.getStackTraceString(e));
+      }
     }
-    */
 
-    try
-    {
+    /**
+     * APQHandlers sets handlers for About, Preferences and Quit actions peculiar to
+     * macOS's application menu. APQHandlers will check to see if a handler is
+     * supported before setting it.
+     */
+    try {
       APQHandlers.setAPQHandlers(this);
-    } catch (Throwable t)
-    {
-      System.out.println("Error setting APQHandlers: " + t.toString());
-      // t.printStackTrace();
+    } catch (Exception e) {
+      System.out.println("Cannot set APQHandlers");
+      // e.printStackTrace();
+    } catch (Throwable t) {
+      Cache.warn("Error setting APQHandlers: " + t.toString());
+      Cache.trace(Cache.getStackTraceString(t));
     }
     setIconImages(ChannelProperties.getIconList());
 
-    addWindowListener(new WindowAdapter()
-    {
+    addWindowListener(new WindowAdapter() {
 
       @Override
-      public void windowClosing(WindowEvent ev)
-      {
+      public void windowClosing(WindowEvent ev) {
         quit();
       }
     });
@@ -433,8 +422,6 @@ public class Desktop extends jalview.jbgui.GDesktop
     showMemusage.setSelected(selmemusage);
     desktop.setBackground(Color.white);
 
-    this.setIconImages(ChannelProperties.getIconList());
-
     getContentPane().setLayout(new BorderLayout());
     // alternate config - have scrollbars - see notes in JAL-153
     // JScrollPane sp = new JScrollPane();
@@ -442,8 +429,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     // getContentPane().add(sp, BorderLayout.CENTER);
 
     // BH 2018 - just an experiment to try unclipped JInternalFrames.
-    if (Platform.isJS())
-    {
+    if (Platform.isJS()) {
       getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
     }
 
@@ -452,20 +438,14 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     // This line prevents Windows Look&Feel resizing all new windows to maximum
     // if previous window was maximised
-    desktop.setDesktopManager(new MyDesktopManager(
-            (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
-                    : Platform.isAMacAndNotJS()
-                            ? new AquaInternalFrameManager(
-                                    desktop.getDesktopManager())
-                            : desktop.getDesktopManager())));
+    desktop.setDesktopManager(new MyDesktopManager((Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
+        : Platform.isAMacAndNotJS() ? new AquaInternalFrameManager(desktop.getDesktopManager())
+            : desktop.getDesktopManager())));
 
     Rectangle dims = getLastKnownDimensions("");
-    if (dims != null)
-    {
+    if (dims != null) {
       setBounds(dims);
-    }
-    else
-    {
+    } else {
       Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
       int xPos = Math.max(5, (screenSize.width - 900) / 2);
       int yPos = Math.max(5, (screenSize.height - 650) / 2);
@@ -492,13 +472,10 @@ public class Desktop extends jalview.jbgui.GDesktop
       checkURLLinks();
 
       // Spawn a thread that shows the splashscreen
-      if (!nosplash)
-      {
-        SwingUtilities.invokeLater(new Runnable()
-        {
+      if (!nosplash) {
+        SwingUtilities.invokeLater(new Runnable() {
           @Override
-          public void run()
-          {
+          public void run() {
             new SplashScreen(true);
           }
         });
@@ -507,50 +484,39 @@ public class Desktop extends jalview.jbgui.GDesktop
       // Thread off a new instance of the file chooser - this reduces the time
       // it
       // takes to open it later on.
-      new Thread(new Runnable()
-      {
+      new Thread(new Runnable() {
         @Override
-        public void run()
-        {
+        public void run() {
           Cache.log.debug("Filechooser init thread started.");
           String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
-          JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
-                  fileFormat);
+          JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat);
           Cache.log.debug("Filechooser init thread finished.");
         }
       }).start();
       // Add the service change listener
-      changeSupport.addJalviewPropertyChangeListener("services",
-              new PropertyChangeListener()
-              {
+      changeSupport.addJalviewPropertyChangeListener("services", new PropertyChangeListener() {
 
-                @Override
-                public void propertyChange(PropertyChangeEvent evt)
-                {
-                  Cache.log.debug("Firing service changed event for "
-                          + evt.getNewValue());
-                  JalviewServicesChanged(evt);
-                }
-              });
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+          Cache.log.debug("Firing service changed event for " + evt.getNewValue());
+          JalviewServicesChanged(evt);
+        }
+      });
     }
 
     this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
 
-    this.addWindowListener(new WindowAdapter()
-    {
+    this.addWindowListener(new WindowAdapter() {
       @Override
-      public void windowClosing(WindowEvent evt)
-      {
+      public void windowClosing(WindowEvent evt) {
         quit();
       }
     });
 
     MouseAdapter ma;
-    this.addMouseListener(ma = new MouseAdapter()
-    {
+    this.addMouseListener(ma = new MouseAdapter() {
       @Override
-      public void mousePressed(MouseEvent evt)
-      {
+      public void mousePressed(MouseEvent evt) {
         if (evt.isPopupTrigger()) // Mac
         {
           showPasteMenu(evt.getX(), evt.getY());
@@ -558,8 +524,7 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
 
       @Override
-      public void mouseReleased(MouseEvent evt)
-      {
+      public void mouseReleased(MouseEvent evt) {
         if (evt.isPopupTrigger()) // Windows
         {
           showPasteMenu(evt.getX(), evt.getY());
@@ -575,44 +540,31 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @return
    */
-  public boolean showExperimental()
-  {
-    String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
-            Boolean.FALSE.toString());
+  public boolean showExperimental() {
+    String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES, Boolean.FALSE.toString());
     return Boolean.valueOf(experimental).booleanValue();
   }
 
-  public void doConfigureStructurePrefs()
-  {
+  public void doConfigureStructurePrefs() {
     // configure services
-    StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(this);
-    if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
-    {
-      ssm.setAddTempFacAnnot(
-              Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
-      ssm.setProcessSecondaryStructure(
-              Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
-      ssm.setSecStructServices(
-              Cache.getDefault(Preferences.USE_RNAVIEW, true));
-    }
-    else
-    {
+    StructureSelectionManager ssm = StructureSelectionManager.getStructureSelectionManager(this);
+    if (Cache.getDefault(Preferences.ADD_SS_ANN, true)) {
+      ssm.setAddTempFacAnnot(Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
+      ssm.setProcessSecondaryStructure(Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
+      ssm.setSecStructServices(Cache.getDefault(Preferences.USE_RNAVIEW, true));
+    } else {
       ssm.setAddTempFacAnnot(false);
       ssm.setProcessSecondaryStructure(false);
       ssm.setSecStructServices(false);
     }
   }
 
-  public void checkForNews()
-  {
+  public void checkForNews() {
     final Desktop me = this;
     // Thread off the news reader, in case there are connection problems.
-    new Thread(new Runnable()
-    {
+    new Thread(new Runnable() {
       @Override
-      public void run()
-      {
+      public void run() {
         Cache.log.debug("Starting news thread.");
         jvnews = new BlogReader(me);
         showNews.setVisible(true);
@@ -621,49 +573,37 @@ public class Desktop extends jalview.jbgui.GDesktop
     }).start();
   }
 
-  public void getIdentifiersOrgData()
-  {
-    // Thread off the identifiers fetcher
-    new Thread(new Runnable()
-    {
-      @Override
-      public void run()
-      {
-        Cache.log.debug("Downloading data from identifiers.org");
-        try
-        {
-          UrlDownloadClient.download(IdOrgSettings.getUrl(),
-                  IdOrgSettings.getDownloadLocation());
-        } catch (IOException e)
-        {
-          Cache.log.debug("Exception downloading identifiers.org data"
-                  + e.getMessage());
+  public void getIdentifiersOrgData() {
+    if (Cache.getProperty("NOIDENTIFIERSSERVICE") == null) {
+      // Thread off the identifiers fetcher
+      new Thread(new Runnable() {
+        @Override
+        public void run() {
+          Cache.log.debug("Downloading data from identifiers.org");
+          try {
+            UrlDownloadClient.download(IdOrgSettings.getUrl(), IdOrgSettings.getDownloadLocation());
+          } catch (IOException e) {
+            Cache.log.debug("Exception downloading identifiers.org data" + e.getMessage());
+          }
         }
-      }
-    }).start();
-
+      }).start();
+    }
   }
 
   @Override
-  protected void showNews_actionPerformed(ActionEvent e)
-  {
+  protected void showNews_actionPerformed(ActionEvent e) {
     showNews(showNews.isSelected());
   }
 
-  void showNews(boolean visible)
-  {
+  void showNews(boolean visible) {
     Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
     showNews.setSelected(visible);
-    if (visible && !jvnews.isVisible())
-    {
-      new Thread(new Runnable()
-      {
+    if (visible && !jvnews.isVisible()) {
+      new Thread(new Runnable() {
         @Override
-        public void run()
-        {
+        public void run() {
           long now = System.currentTimeMillis();
-          Desktop.instance.setProgressBar(
-                  MessageManager.getString("status.refreshing_news"), now);
+          Desktop.instance.setProgressBar(MessageManager.getString("status.refreshing_news"), now);
           jvnews.refreshNews();
           Desktop.instance.setProgressBar(null, now);
           jvnews.showNews();
@@ -675,52 +615,42 @@ public class Desktop extends jalview.jbgui.GDesktop
   /**
    * recover the last known dimensions for a jalview window
    * 
-   * @param windowName
-   *          - empty string is desktop, all other windows have unique prefix
+   * @param windowName - empty string is desktop, all other windows have unique
+   *                   prefix
    * @return null or last known dimensions scaled to current geometry (if last
    *         window geom was known)
    */
-  Rectangle getLastKnownDimensions(String windowName)
-  {
+  Rectangle getLastKnownDimensions(String windowName) {
     // TODO: lock aspect ratio for scaling desktop Bug #0058199
     Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
     String x = Cache.getProperty(windowName + "SCREEN_X");
     String y = Cache.getProperty(windowName + "SCREEN_Y");
     String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
     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 (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
-      {
+    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 (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(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
-        double sh = ((1f * screenSize.height) / (1f * Integer
-                .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
+        double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
+        double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
         // rescale the bounds depending upon the current screen geometry.
         ix = (int) (ix * sw);
         iw = (int) (iw * sw);
         iy = (int) (iy * sh);
         ih = (int) (ih * sh);
-        while (ix >= screenSize.width)
-        {
-          Cache.log.debug(
-                  "Window geometry location recall error: shifting horizontal to within screenbounds.");
+        while (ix >= screenSize.width) {
+          Cache.log.debug("Window geometry location recall error: shifting horizontal to within screenbounds.");
           ix -= screenSize.width;
         }
-        while (iy >= screenSize.height)
-        {
-          Cache.log.debug(
-                  "Window geometry location recall error: shifting vertical to within screenbounds.");
+        while (iy >= screenSize.height) {
+          Cache.log.debug("Window geometry location recall error: shifting vertical to within screenbounds.");
           iy -= screenSize.height;
         }
-        Cache.log.debug(
-                "Got last known dimensions for " + windowName + ": x:" + ix
-                        + " y:" + iy + " width:" + iw + " height:" + ih);
+        Cache.log.debug("Got last known dimensions for " + windowName + ": x:" + ix + " y:" + iy + " width:" + iw
+            + " height:" + ih);
       }
       // return dimensions for new instance
       return new Rectangle(ix, iy, iw, ih);
@@ -728,16 +658,12 @@ public class Desktop extends jalview.jbgui.GDesktop
     return null;
   }
 
-  void showPasteMenu(int x, int y)
-  {
+  void showPasteMenu(int x, int y) {
     JPopupMenu popup = new JPopupMenu();
-    JMenuItem item = new JMenuItem(
-            MessageManager.getString("label.paste_new_window"));
-    item.addActionListener(new ActionListener()
-    {
+    JMenuItem item = new JMenuItem(MessageManager.getString("label.paste_new_window"));
+    item.addActionListener(new ActionListener() {
       @Override
-      public void actionPerformed(ActionEvent evt)
-      {
+      public void actionPerformed(ActionEvent evt) {
         paste();
       }
     });
@@ -746,115 +672,79 @@ public class Desktop extends jalview.jbgui.GDesktop
     popup.show(this, x, y);
   }
 
-  public void paste()
-  {
-    try
-    {
+  public void paste() {
+    try {
       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
       Transferable contents = c.getContents(this);
 
-      if (contents != null)
-      {
-        String file = (String) contents
-                .getTransferData(DataFlavor.stringFlavor);
+      if (contents != null) {
+        String file = (String) contents.getTransferData(DataFlavor.stringFlavor);
 
-        FileFormatI format = new IdentifyFile().identify(file,
-                DataSourceType.PASTE);
+        FileFormatI format = new IdentifyFile().identify(file, DataSourceType.PASTE);
 
         new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
 
       }
-    } catch (Exception ex)
-    {
-      System.out.println(
-              "Unable to paste alignment from system clipboard:\n" + ex);
+    } catch (Exception ex) {
+      System.out.println("Unable to paste alignment from system clipboard:\n" + ex);
     }
   }
 
   /**
    * Adds and opens the given frame to the desktop
    * 
-   * @param frame
-   *          Frame to show
-   * @param title
-   *          Visible Title
-   * @param w
-   *          width
-   * @param h
-   *          height
-   */
-  public static synchronized void addInternalFrame(
-          final JInternalFrame frame, String title, int w, int h)
-  {
+   * @param frame Frame to show
+   * @param title Visible Title
+   * @param w     width
+   * @param h     height
+   */
+  public static synchronized void addInternalFrame(final JInternalFrame frame, String title, int w, int h) {
     addInternalFrame(frame, title, true, w, h, true, false);
   }
 
   /**
    * Add an internal frame to the Jalview desktop
    * 
-   * @param frame
-   *          Frame to show
-   * @param title
-   *          Visible Title
-   * @param makeVisible
-   *          When true, display frame immediately, otherwise, caller must call
-   *          setVisible themselves.
-   * @param w
-   *          width
-   * @param h
-   *          height
-   */
-  public static synchronized void addInternalFrame(
-          final JInternalFrame frame, String title, boolean makeVisible,
-          int w, int h)
-  {
+   * @param frame       Frame to show
+   * @param title       Visible Title
+   * @param makeVisible When true, display frame immediately, otherwise, caller
+   *                    must call setVisible themselves.
+   * @param w           width
+   * @param h           height
+   */
+  public static synchronized void addInternalFrame(final JInternalFrame frame, String title, boolean makeVisible, int w,
+      int h) {
     addInternalFrame(frame, title, makeVisible, w, h, true, false);
   }
 
   /**
    * Add an internal frame to the Jalview desktop and make it visible
    * 
-   * @param frame
-   *          Frame to show
-   * @param title
-   *          Visible Title
-   * @param w
-   *          width
-   * @param h
-   *          height
-   * @param resizable
-   *          Allow resize
-   */
-  public static synchronized void addInternalFrame(
-          final JInternalFrame frame, String title, int w, int h,
-          boolean resizable)
-  {
+   * @param frame     Frame to show
+   * @param title     Visible Title
+   * @param w         width
+   * @param h         height
+   * @param resizable Allow resize
+   */
+  public static synchronized void addInternalFrame(final JInternalFrame frame, String title, int w, int h,
+      boolean resizable) {
     addInternalFrame(frame, title, true, w, h, resizable, false);
   }
 
   /**
    * Add an internal frame to the Jalview desktop
    * 
-   * @param frame
-   *          Frame to show
-   * @param title
-   *          Visible Title
-   * @param makeVisible
-   *          When true, display frame immediately, otherwise, caller must call
-   *          setVisible themselves.
-   * @param w
-   *          width
-   * @param h
-   *          height
-   * @param resizable
-   *          Allow resize
-   * @param ignoreMinSize
-   *          Do not set the default minimum size for frame
-   */
-  public static synchronized void addInternalFrame(
-          final JInternalFrame frame, String title, boolean makeVisible,
-          int w, int h, boolean resizable, boolean ignoreMinSize)
-  {
+   * @param frame         Frame to show
+   * @param title         Visible Title
+   * @param makeVisible   When true, display frame immediately, otherwise, caller
+   *                      must call setVisible themselves.
+   * @param w             width
+   * @param h             height
+   * @param resizable     Allow resize
+   * @param ignoreMinSize Do not set the default minimum size for frame
+   */
+  public static synchronized void addInternalFrame(final JInternalFrame frame, String title, boolean makeVisible, int w,
+      int h, boolean resizable, boolean ignoreMinSize) {
 
     // TODO: allow callers to determine X and Y position of frame (eg. via
     // bounds object).
@@ -862,8 +752,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     // the current window title
 
     frame.setTitle(title);
-    if (frame.getWidth() < 1 || frame.getHeight() < 1)
-    {
+    if (frame.getWidth() < 1 || frame.getHeight() < 1) {
       frame.setSize(w, h);
     }
     // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
@@ -871,26 +760,21 @@ public class Desktop extends jalview.jbgui.GDesktop
     // IF JALVIEW IS RUNNING HEADLESS
     // ///////////////////////////////////////////////
     if (instance == null || (System.getProperty("java.awt.headless") != null
-            && System.getProperty("java.awt.headless").equals("true")))
-    {
+        && System.getProperty("java.awt.headless").equals("true"))) {
       return;
     }
 
     openFrameCount++;
 
-    if (!ignoreMinSize)
-    {
-      frame.setMinimumSize(
-              new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
+    if (!ignoreMinSize) {
+      frame.setMinimumSize(new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
 
       // Set default dimension for Alignment Frame window.
       // The Alignment Frame window could be added from a number of places,
       // hence,
       // I did this here in order not to miss out on any Alignment frame.
-      if (frame instanceof AlignFrame)
-      {
-        frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
-                ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
+      if (frame instanceof AlignFrame) {
+        frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH, ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
       }
     }
 
@@ -901,27 +785,21 @@ public class Desktop extends jalview.jbgui.GDesktop
     frame.setIconifiable(resizable);
     frame.setOpaque(Platform.isJS());
 
-    if (frame.getX() < 1 && frame.getY() < 1)
-    {
-      frame.setLocation(xOffset * openFrameCount,
-              yOffset * ((openFrameCount - 1) % 10) + yOffset);
+    if (frame.getX() < 1 && frame.getY() < 1) {
+      frame.setLocation(xOffset * openFrameCount, yOffset * ((openFrameCount - 1) % 10) + yOffset);
     }
 
     /*
-     * add an entry for the new frame in the Window menu 
-     * (and remove it when the frame is closed)
+     * add an entry for the new frame in the Window menu (and remove it when the
+     * frame is closed)
      */
     final JMenuItem menuItem = new JMenuItem(title);
-    frame.addInternalFrameListener(new InternalFrameAdapter()
-    {
+    frame.addInternalFrameListener(new InternalFrameAdapter() {
       @Override
-      public void internalFrameActivated(InternalFrameEvent evt)
-      {
+      public void internalFrameActivated(InternalFrameEvent evt) {
         JInternalFrame itf = desktop.getSelectedFrame();
-        if (itf != null)
-        {
-          if (itf instanceof AlignFrame)
-          {
+        if (itf != null) {
+          if (itf instanceof AlignFrame) {
             Jalview.setCurrentAlignFrame((AlignFrame) itf);
           }
           itf.requestFocus();
@@ -929,42 +807,34 @@ public class Desktop extends jalview.jbgui.GDesktop
       }
 
       @Override
-      public void internalFrameClosed(InternalFrameEvent evt)
-      {
+      public void internalFrameClosed(InternalFrameEvent evt) {
         PaintRefresher.RemoveComponent(frame);
 
         /*
-         * defensive check to prevent frames being
-         * added half off the window
+         * defensive check to prevent frames being added half off the window
          */
-        if (openFrameCount > 0)
-        {
+        if (openFrameCount > 0) {
           openFrameCount--;
         }
 
         /*
          * ensure no reference to alignFrame retained by menu item listener
          */
-        if (menuItem.getActionListeners().length > 0)
-        {
+        if (menuItem.getActionListeners().length > 0) {
           menuItem.removeActionListener(menuItem.getActionListeners()[0]);
         }
         windowMenu.remove(menuItem);
       }
     });
 
-    menuItem.addActionListener(new ActionListener()
-    {
+    menuItem.addActionListener(new ActionListener() {
       @Override
-      public void actionPerformed(ActionEvent e)
-      {
-        try
-        {
+      public void actionPerformed(ActionEvent e) {
+        try {
           frame.setSelected(true);
           frame.setIcon(false);
-        } catch (java.beans.PropertyVetoException ex)
-        {
-          // System.err.println(ex.toString());
+        } catch (java.beans.PropertyVetoException ex) {
+
         }
       }
     });
@@ -976,34 +846,28 @@ public class Desktop extends jalview.jbgui.GDesktop
     windowMenu.add(menuItem);
 
     frame.toFront();
-    try
-    {
+    try {
       frame.setSelected(true);
       frame.requestFocus();
-    } catch (java.beans.PropertyVetoException ve)
-    {
-    } catch (java.lang.ClassCastException cex)
-    {
+    } catch (java.beans.PropertyVetoException ve) {
+    } catch (java.lang.ClassCastException cex) {
       Cache.log.warn(
-              "Squashed a possible GUI implementation error. If you can recreate this, please look at https://issues.jalview.org/browse/JAL-869",
-              cex);
+          "Squashed a possible GUI implementation error. If you can recreate this, please look at https://issues.jalview.org/browse/JAL-869",
+          cex);
     }
   }
 
   /**
-   * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
-   * the window
+   * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
+   * window
    * 
    * @param frame
    */
-  private static void setKeyBindings(JInternalFrame frame)
-  {
+  private static void setKeyBindings(JInternalFrame frame) {
     @SuppressWarnings("serial")
-    final Action closeAction = new AbstractAction()
-    {
+    final Action closeAction = new AbstractAction() {
       @Override
-      public void actionPerformed(ActionEvent e)
-      {
+      public void actionPerformed(ActionEvent e) {
         frame.dispose();
       }
     };
@@ -1011,13 +875,10 @@ public class Desktop extends jalview.jbgui.GDesktop
     /*
      * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
      */
-    KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
-            InputEvent.CTRL_DOWN_MASK);
-    KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
-            ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
+    KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK);
+    KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W, ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
 
-    InputMap inputMap = frame
-            .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
+    InputMap inputMap = frame.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
     String ctrlW = ctrlWKey.toString();
     inputMap.put(ctrlWKey, ctrlW);
     inputMap.put(cmdWKey, ctrlW);
@@ -1027,10 +888,8 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   @Override
-  public void lostOwnership(Clipboard clipboard, Transferable contents)
-  {
-    if (!internalCopy)
-    {
+  public void lostOwnership(Clipboard clipboard, Transferable contents) {
+    if (!internalCopy) {
       Desktop.jalviewClipboard = null;
     }
 
@@ -1038,34 +897,28 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   @Override
-  public void dragEnter(DropTargetDragEvent evt)
-  {
+  public void dragEnter(DropTargetDragEvent evt) {
   }
 
   @Override
-  public void dragExit(DropTargetEvent evt)
-  {
+  public void dragExit(DropTargetEvent evt) {
   }
 
   @Override
-  public void dragOver(DropTargetDragEvent evt)
-  {
+  public void dragOver(DropTargetDragEvent evt) {
   }
 
   @Override
-  public void dropActionChanged(DropTargetDragEvent evt)
-  {
+  public void dropActionChanged(DropTargetDragEvent evt) {
   }
 
   /**
    * DOCUMENT ME!
    * 
-   * @param evt
-   *          DOCUMENT ME!
+   * @param evt DOCUMENT ME!
    */
   @Override
-  public void drop(DropTargetDropEvent evt)
-  {
+  public void drop(DropTargetDropEvent evt) {
     boolean success = true;
     // JAL-1552 - acceptDrop required before getTransferable call for
     // Java's Transferable for native dnd
@@ -1074,47 +927,35 @@ public class Desktop extends jalview.jbgui.GDesktop
     List<Object> files = new ArrayList<>();
     List<DataSourceType> protocols = new ArrayList<>();
 
-    try
-    {
+    try {
       Desktop.transferFromDropTarget(files, protocols, evt, t);
-    } catch (Exception e)
-    {
+    } catch (Exception e) {
       e.printStackTrace();
       success = false;
     }
 
-    if (files != null)
-    {
-      try
-      {
-        for (int i = 0; i < files.size(); i++)
-        {
+    if (files != null) {
+      try {
+        for (int i = 0; i < files.size(); i++) {
           // BH 2018 File or String
           Object file = files.get(i);
           String fileName = file.toString();
-          DataSourceType protocol = (protocols == null)
-                  ? DataSourceType.FILE
-                  : protocols.get(i);
+          DataSourceType protocol = (protocols == null) ? DataSourceType.FILE : protocols.get(i);
           FileFormatI format = null;
 
-          if (fileName.endsWith(".jar"))
-          {
+          if (fileName.endsWith(".jar")) {
             format = FileFormat.Jalview;
 
-          }
-          else
-          {
+          } else {
             format = new IdentifyFile().identify(file, protocol);
           }
-          if (file instanceof File)
-          {
+          if (file instanceof File) {
             Platform.cacheFileData((File) file);
           }
           new FileLoader().LoadFile(null, file, protocol, format);
 
         }
-      } catch (Exception ex)
-      {
+      } catch (Exception ex) {
         success = false;
       }
     }
@@ -1125,27 +966,21 @@ public class Desktop extends jalview.jbgui.GDesktop
   /**
    * DOCUMENT ME!
    * 
-   * @param e
-   *          DOCUMENT ME!
+   * @param e DOCUMENT ME!
    */
   @Override
-  public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
-  {
+  public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport) {
     String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
-    JalviewFileChooser chooser = JalviewFileChooser.forRead(
-            Cache.getProperty("LAST_DIRECTORY"), fileFormat,
-            BackupFiles.getEnabled());
+    JalviewFileChooser chooser = JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat,
+        BackupFiles.getEnabled());
 
     chooser.setFileView(new JalviewFileView());
-    chooser.setDialogTitle(
-            MessageManager.getString("label.open_local_file"));
+    chooser.setDialogTitle(MessageManager.getString("label.open_local_file"));
     chooser.setToolTipText(MessageManager.getString("action.open"));
 
-    chooser.setResponseHandler(0, new Runnable()
-    {
+    chooser.setResponseHandler(0, new Runnable() {
       @Override
-      public void run()
-      {
+      public void run() {
         File selectedFile = chooser.getSelectedFile();
         Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
 
@@ -1153,23 +988,18 @@ public class Desktop extends jalview.jbgui.GDesktop
 
         /*
          * Call IdentifyFile to verify the file contains what its extension implies.
-         * Skip this step for dynamically added file formats, because
-         * IdentifyFile does not know how to recognise them.
+         * Skip this step for dynamically added file formats, because IdentifyFile does
+         * not know how to recognise them.
          */
-        if (FileFormats.getInstance().isIdentifiable(format))
-        {
-          try
-          {
-            format = new IdentifyFile().identify(selectedFile,
-                    DataSourceType.FILE);
-          } catch (FileFormatException e)
-          {
+        if (FileFormats.getInstance().isIdentifiable(format)) {
+          try {
+            format = new IdentifyFile().identify(selectedFile, DataSourceType.FILE);
+          } catch (FileFormatException e) {
             // format = null; //??
           }
         }
 
-        new FileLoader().LoadFile(viewport, selectedFile,
-                DataSourceType.FILE, format);
+        new FileLoader().LoadFile(viewport, selectedFile, DataSourceType.FILE, format);
       }
     });
     chooser.showOpenDialog(this);
@@ -1181,28 +1011,23 @@ public class Desktop extends jalview.jbgui.GDesktop
    * @param viewport
    */
   @Override
-  public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
-  {
+  public void inputURLMenuItem_actionPerformed(AlignViewport viewport) {
     // This construct allows us to have a wider textfield
     // for viewing
-    JLabel label = new JLabel(
-            MessageManager.getString("label.input_file_url"));
+    JLabel label = new JLabel(MessageManager.getString("label.input_file_url"));
 
     JPanel panel = new JPanel(new GridLayout(2, 1));
     panel.add(label);
 
     /*
-     * the URL to fetch is
-     * Java: an editable combobox with history
-     * JS: (pending JAL-3038) a plain text field
+     * the URL to fetch is input in Java: an editable combobox with history JS:
+     * (pending JAL-3038) a plain text field
      */
     JComponent history;
     String urlBase = "https://www.";
-    if (Platform.isJS())
-    {
+    if (Platform.isJS()) {
       history = new JTextField(urlBase, 35);
-    }
-    else
+    } else
     /**
      * Java only
      * 
@@ -1214,10 +1039,8 @@ public class Desktop extends jalview.jbgui.GDesktop
       asCombo.setEditable(true);
       asCombo.addItem(urlBase);
       String historyItems = Cache.getProperty("RECENT_URL");
-      if (historyItems != null)
-      {
-        for (String token : historyItems.split("\\t"))
-        {
+      if (historyItems != null) {
+        for (String token : historyItems.split("\\t")) {
           asCombo.addItem(token);
         }
       }
@@ -1227,122 +1050,87 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     Object[] options = new Object[] { MessageManager.getString("action.ok"),
         MessageManager.getString("action.cancel") };
-    Runnable action = new Runnable()
-    {
+    Runnable action = new Runnable() {
       @Override
-      public void run()
-      {
+      public void run() {
         @SuppressWarnings("unchecked")
-        String url = (history instanceof JTextField
-                ? ((JTextField) history).getText()
-                : ((JComboBox<String>) history).getSelectedItem()
-                        .toString());
-
-        if (url.toLowerCase().endsWith(".jar"))
-        {
-          if (viewport != null)
-          {
-            new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
-                    FileFormat.Jalview);
+        String url = (history instanceof JTextField ? ((JTextField) history).getText()
+            : ((JComboBox<String>) history).getEditor().getItem().toString().trim());
+
+        if (url.toLowerCase(Locale.ROOT).endsWith(".jar")) {
+          if (viewport != null) {
+            new FileLoader().LoadFile(viewport, url, DataSourceType.URL, FileFormat.Jalview);
+          } else {
+            new FileLoader().LoadFile(url, DataSourceType.URL, FileFormat.Jalview);
           }
-          else
-          {
-            new FileLoader().LoadFile(url, DataSourceType.URL,
-                    FileFormat.Jalview);
-          }
-        }
-        else
-        {
+        } else {
           FileFormatI format = null;
-          try
-          {
+          try {
             format = new IdentifyFile().identify(url, DataSourceType.URL);
-          } catch (FileFormatException e)
-          {
+          } catch (FileFormatException e) {
             // TODO revise error handling, distinguish between
             // URL not found and response not valid
           }
 
-          if (format == null)
-          {
-            String msg = MessageManager
-                    .formatMessage("label.couldnt_locate", url);
+          if (format == null) {
+            String msg = MessageManager.formatMessage("label.couldnt_locate", url);
             JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
-                    MessageManager.getString("label.url_not_found"),
-                    JvOptionPane.WARNING_MESSAGE);
+                MessageManager.getString("label.url_not_found"), JvOptionPane.WARNING_MESSAGE);
 
             return;
           }
 
-          if (viewport != null)
-          {
-            new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
-                    format);
-          }
-          else
-          {
+          if (viewport != null) {
+            new FileLoader().LoadFile(viewport, url, DataSourceType.URL, format);
+          } else {
             new FileLoader().LoadFile(url, DataSourceType.URL, format);
           }
         }
       }
     };
-    String dialogOption = MessageManager
-            .getString("label.input_alignment_from_url");
-    JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
-            .showInternalDialog(panel, dialogOption,
-                    JvOptionPane.YES_NO_CANCEL_OPTION,
-                    JvOptionPane.PLAIN_MESSAGE, null, options,
-                    MessageManager.getString("action.ok"));
+    String dialogOption = MessageManager.getString("label.input_alignment_from_url");
+    JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action).showInternalDialog(panel, dialogOption,
+        JvOptionPane.YES_NO_CANCEL_OPTION, JvOptionPane.PLAIN_MESSAGE, null, options,
+        MessageManager.getString("action.ok"));
   }
 
   /**
    * Opens the CutAndPaste window for the user to paste an alignment in to
    * 
-   * @param viewPanel
-   *          - if not null, the pasted alignment is added to the current
-   *          alignment; if null, to a new alignment window
+   * @param viewPanel - if not null, the pasted alignment is added to the current
+   *                  alignment; if null, to a new alignment window
    */
   @Override
-  public void inputTextboxMenuItem_actionPerformed(
-          AlignmentViewPanel viewPanel)
-  {
+  public void inputTextboxMenuItem_actionPerformed(AlignmentViewPanel viewPanel) {
     CutAndPasteTransfer cap = new CutAndPasteTransfer();
     cap.setForInput(viewPanel);
-    Desktop.addInternalFrame(cap,
-            MessageManager.getString("label.cut_paste_alignmen_file"), true,
-            600, 500);
+    Desktop.addInternalFrame(cap, MessageManager.getString("label.cut_paste_alignmen_file"), true, 600, 500);
   }
 
   /*
    * Exit the program
    */
   @Override
-  public void quit()
-  {
+  public void quit() {
     Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
     Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
     Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
-    storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
-            getWidth(), getHeight()));
+    storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y, getWidth(), getHeight()));
 
-    if (jconsole != null)
-    {
+    if (jconsole != null) {
       storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
       jconsole.stopConsole();
     }
-    if (jvnews != null)
-    {
+    if (jvnews != null) {
       storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
 
     }
-    if (dialogExecutor != null)
-    {
+    if (dialogExecutor != null) {
       dialogExecutor.shutdownNow();
     }
     closeAll_actionPerformed(null);
 
-    if (groovyConsole != null)
-    {
+    if (groovyConsole != null) {
       // suppress a possible repeat prompt to save script
       groovyConsole.setDirty(false);
       groovyConsole.exit();
@@ -1350,11 +1138,9 @@ public class Desktop extends jalview.jbgui.GDesktop
     System.exit(0);
   }
 
-  private void storeLastKnownDimensions(String string, Rectangle jc)
-  {
-    Cache.log.debug("Storing last known dimensions for " + string + ": x:"
-            + jc.x + " y:" + jc.y + " width:" + jc.width + " height:"
-            + jc.height);
+  private void storeLastKnownDimensions(String string, Rectangle jc) {
+    Cache.log.debug("Storing last known dimensions for " + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
+        + " height:" + jc.height);
 
     Cache.setProperty(string + "SCREEN_X", jc.x + "");
     Cache.setProperty(string + "SCREEN_Y", jc.y + "");
@@ -1365,17 +1151,13 @@ public class Desktop extends jalview.jbgui.GDesktop
   /**
    * DOCUMENT ME!
    * 
-   * @param e
-   *          DOCUMENT ME!
+   * @param e DOCUMENT ME!
    */
   @Override
-  public void aboutMenuItem_actionPerformed(ActionEvent e)
-  {
-    new Thread(new Runnable()
-    {
+  public void aboutMenuItem_actionPerformed(ActionEvent e) {
+    new Thread(new Runnable() {
       @Override
-      public void run()
-      {
+      public void run() {
         new SplashScreen(false);
       }
     }).start();
@@ -1383,48 +1165,35 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   /**
    * Returns the html text for the About screen, including any available version
-   * number, build details, author details and citation reference, but without
-   * the enclosing {@code html} tags
+   * number, build details, author details and citation reference, but without the
+   * enclosing {@code html} tags
    * 
    * @return
    */
-  public String getAboutMessage()
-  {
+  public String getAboutMessage() {
     StringBuilder message = new StringBuilder(1024);
-    message.append("<div style=\"font-family: sans-serif;\">")
-            .append("<h1><strong>Version: ")
-            .append(Cache.getProperty("VERSION")).append("</strong></h1>")
-            .append("<strong>Built: <em>")
-            .append(Cache.getDefault("BUILD_DATE", "unknown"))
-            .append("</em> from ").append(Cache.getBuildDetailsForSplash())
-            .append("</strong>");
+    message.append("<div style=\"font-family: sans-serif;\">").append("<h1><strong>Version: ")
+        .append(Cache.getProperty("VERSION")).append("</strong></h1>").append("<strong>Built: <em>")
+        .append(Cache.getDefault("BUILD_DATE", "unknown")).append("</em> from ")
+        .append(Cache.getBuildDetailsForSplash()).append("</strong>");
 
     String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
-    if (latestVersion.equals("Checking"))
-    {
+    if (latestVersion.equals("Checking")) {
       // JBP removed this message for 2.11: May be reinstated in future version
       // message.append("<br>...Checking latest version...</br>");
-    }
-    else if (!latestVersion.equals(Cache.getProperty("VERSION")))
-    {
+    } else if (!latestVersion.equals(Cache.getProperty("VERSION"))) {
       boolean red = false;
-      if (Cache.getProperty("VERSION").toLowerCase()
-              .indexOf("automated build") == -1)
-      {
+      if (Cache.getProperty("VERSION").toLowerCase(Locale.ROOT).indexOf("automated build") == -1) {
         red = true;
         // Displayed when code version and jnlp version do not match and code
         // version is not a development build
         message.append("<div style=\"color: #FF0000;font-style: bold;\">");
       }
 
-      message.append("<br>!! Version ")
-              .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
-              .append(" is available for download from ")
-              .append(Cache.getDefault("www.jalview.org",
-                      "https://www.jalview.org"))
-              .append(" !!");
-      if (red)
-      {
+      message.append("<br>!! Version ").append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
+          .append(" is available for download from ")
+          .append(Cache.getDefault("www.jalview.org", "https://www.jalview.org")).append(" !!");
+      if (red) {
         message.append("</div>");
       }
     }
@@ -1441,15 +1210,11 @@ public class Desktop extends jalview.jbgui.GDesktop
    * Action on requesting Help documentation
    */
   @Override
-  public void documentationMenuItem_actionPerformed()
-  {
-    try
-    {
-      if (Platform.isJS())
-      {
+  public void documentationMenuItem_actionPerformed() {
+    try {
+      if (Platform.isJS()) {
         BrowserLauncher.openURL("https://www.jalview.org/help.html");
-      }
-      else
+      } else
       /**
        * Java only
        * 
@@ -1458,24 +1223,19 @@ public class Desktop extends jalview.jbgui.GDesktop
       {
         Help.showHelpWindow();
       }
-    } catch (Exception ex)
-    {
+    } catch (Exception ex) {
       System.err.println("Error opening help: " + ex.getMessage());
     }
   }
 
   @Override
-  public void closeAll_actionPerformed(ActionEvent e)
-  {
+  public void closeAll_actionPerformed(ActionEvent e) {
     // TODO show a progress bar while closing?
     JInternalFrame[] frames = desktop.getAllFrames();
-    for (int i = 0; i < frames.length; i++)
-    {
-      try
-      {
+    for (int i = 0; i < frames.length; i++) {
+      try {
         frames[i].setClosed(true);
-      } catch (java.beans.PropertyVetoException ex)
-      {
+      } catch (java.beans.PropertyVetoException ex) {
       }
     }
     Jalview.setCurrentAlignFrame(null);
@@ -1485,28 +1245,23 @@ public class Desktop extends jalview.jbgui.GDesktop
      * reset state of singleton objects as appropriate (clear down session state
      * when all windows are closed)
      */
-    StructureSelectionManager ssm = StructureSelectionManager
-            .getStructureSelectionManager(this);
-    if (ssm != null)
-    {
+    StructureSelectionManager ssm = StructureSelectionManager.getStructureSelectionManager(this);
+    if (ssm != null) {
       ssm.resetAll();
     }
   }
 
   @Override
-  public void raiseRelated_actionPerformed(ActionEvent e)
-  {
+  public void raiseRelated_actionPerformed(ActionEvent e) {
     reorderAssociatedWindows(false, false);
   }
 
   @Override
-  public void minimizeAssociated_actionPerformed(ActionEvent e)
-  {
+  public void minimizeAssociated_actionPerformed(ActionEvent e) {
     reorderAssociatedWindows(true, false);
   }
 
-  void closeAssociatedWindows()
-  {
+  void closeAssociatedWindows() {
     reorderAssociatedWindows(false, true);
   }
 
@@ -1517,8 +1272,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    * ActionEvent)
    */
   @Override
-  protected void garbageCollect_actionPerformed(ActionEvent e)
-  {
+  protected void garbageCollect_actionPerformed(ActionEvent e) {
     // We simply collect the garbage
     Cache.log.debug("Collecting garbage...");
     System.gc();
@@ -1528,13 +1282,11 @@ public class Desktop extends jalview.jbgui.GDesktop
   /*
    * (non-Javadoc)
    * 
-   * @see
-   * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
-   * )
+   * @see jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.
+   * ActionEvent )
    */
   @Override
-  protected void showMemusage_actionPerformed(ActionEvent e)
-  {
+  protected void showMemusage_actionPerformed(ActionEvent e) {
     desktop.showMemoryUsage(showMemusage.isSelected());
   }
 
@@ -1546,8 +1298,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    * )
    */
   @Override
-  protected void showConsole_actionPerformed(ActionEvent e)
-  {
+  protected void showConsole_actionPerformed(ActionEvent e) {
     showConsole(showConsole.isSelected());
   }
 
@@ -1558,89 +1309,61 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @param selected
    */
-  void showConsole(boolean selected)
-  {
+  void showConsole(boolean selected) {
     // TODO: decide if we should update properties file
     if (jconsole != null) // BH 2018
     {
       showConsole.setSelected(selected);
-      Cache.setProperty("SHOW_JAVA_CONSOLE",
-              Boolean.valueOf(selected).toString());
+      Cache.setProperty("SHOW_JAVA_CONSOLE", Boolean.valueOf(selected).toString());
       jconsole.setVisible(selected);
     }
   }
 
-  void reorderAssociatedWindows(boolean minimize, boolean close)
-  {
+  void reorderAssociatedWindows(boolean minimize, boolean close) {
     JInternalFrame[] frames = desktop.getAllFrames();
-    if (frames == null || frames.length < 1)
-    {
+    if (frames == null || frames.length < 1) {
       return;
     }
 
     AlignmentViewport source = null, target = null;
-    if (frames[0] instanceof AlignFrame)
-    {
+    if (frames[0] instanceof AlignFrame) {
       source = ((AlignFrame) frames[0]).getCurrentView();
-    }
-    else if (frames[0] instanceof TreePanel)
-    {
+    } else if (frames[0] instanceof TreePanel) {
       source = ((TreePanel) frames[0]).getViewPort();
-    }
-    else if (frames[0] instanceof PCAPanel)
-    {
+    } else if (frames[0] instanceof PCAPanel) {
       source = ((PCAPanel) frames[0]).av;
-    }
-    else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
-    {
+    } else if (frames[0].getContentPane() instanceof PairwiseAlignPanel) {
       source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
     }
 
-    if (source != null)
-    {
-      for (int i = 0; i < frames.length; i++)
-      {
+    if (source != null) {
+      for (int i = 0; i < frames.length; i++) {
         target = null;
-        if (frames[i] == null)
-        {
+        if (frames[i] == null) {
           continue;
         }
-        if (frames[i] instanceof AlignFrame)
-        {
+        if (frames[i] instanceof AlignFrame) {
           target = ((AlignFrame) frames[i]).getCurrentView();
-        }
-        else if (frames[i] instanceof TreePanel)
-        {
+        } else if (frames[i] instanceof TreePanel) {
           target = ((TreePanel) frames[i]).getViewPort();
-        }
-        else if (frames[i] instanceof PCAPanel)
-        {
+        } else if (frames[i] instanceof PCAPanel) {
           target = ((PCAPanel) frames[i]).av;
-        }
-        else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
-        {
+        } else if (frames[i].getContentPane() instanceof PairwiseAlignPanel) {
           target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
         }
 
-        if (source == target)
-        {
-          try
-          {
-            if (close)
-            {
+        if (source == target) {
+          try {
+            if (close) {
               frames[i].setClosed(true);
-            }
-            else
-            {
+            } else {
               frames[i].setIcon(minimize);
-              if (!minimize)
-              {
+              if (!minimize) {
                 frames[i].toFront();
               }
             }
 
-          } catch (java.beans.PropertyVetoException ex)
-          {
+          } catch (java.beans.PropertyVetoException ex) {
           }
         }
       }
@@ -1650,12 +1373,10 @@ public class Desktop extends jalview.jbgui.GDesktop
   /**
    * DOCUMENT ME!
    * 
-   * @param e
-   *          DOCUMENT ME!
+   * @param e DOCUMENT ME!
    */
   @Override
-  protected void preferences_actionPerformed(ActionEvent e)
-  {
+  protected void preferences_actionPerformed(ActionEvent e) {
     Preferences.openPreferences();
   }
 
@@ -1664,86 +1385,66 @@ public class Desktop extends jalview.jbgui.GDesktop
    * Jalview project file
    */
   @Override
-  public void saveState_actionPerformed()
-  {
+  public void saveState_actionPerformed() {
     saveState_actionPerformed(false);
   }
 
-  public void saveState_actionPerformed(boolean saveAs)
-  {
+  public void saveState_actionPerformed(boolean saveAs) {
     java.io.File projectFile = getProjectFile();
     // autoSave indicates we already have a file and don't need to ask
-    boolean autoSave = projectFile != null && !saveAs
-            && BackupFiles.getEnabled();
+    boolean autoSave = projectFile != null && !saveAs && BackupFiles.getEnabled();
 
     // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
     // saveAs="+saveAs+", Backups
     // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
 
     boolean approveSave = false;
-    if (!autoSave)
-    {
-      JalviewFileChooser chooser = new JalviewFileChooser("jvp",
-              "Jalview Project");
+    if (!autoSave) {
+      JalviewFileChooser chooser = new JalviewFileChooser("jvp", "Jalview Project");
 
       chooser.setFileView(new JalviewFileView());
       chooser.setDialogTitle(MessageManager.getString("label.save_state"));
 
       int value = chooser.showSaveDialog(this);
 
-      if (value == JalviewFileChooser.APPROVE_OPTION)
-      {
+      if (value == JalviewFileChooser.APPROVE_OPTION) {
         projectFile = chooser.getSelectedFile();
         setProjectFile(projectFile);
         approveSave = true;
       }
     }
 
-    if (approveSave || autoSave)
-    {
+    if (approveSave || autoSave) {
       final Desktop me = this;
       final java.io.File chosenFile = projectFile;
-      new Thread(new Runnable()
-      {
+      new Thread(new Runnable() {
         @Override
-        public void run()
-        {
+        public void run() {
           // TODO: refactor to Jalview desktop session controller action.
-          setProgressBar(MessageManager.formatMessage(
-                  "label.saving_jalview_project", new Object[]
-                  { chosenFile.getName() }), chosenFile.hashCode());
+          setProgressBar(
+              MessageManager.formatMessage("label.saving_jalview_project", new Object[] { chosenFile.getName() }),
+              chosenFile.hashCode());
           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
-          try
-          {
+          try {
             boolean doBackup = BackupFiles.getEnabled();
-            BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
-                    : null;
+            BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
 
-            new Jalview2XML().saveState(
-                    doBackup ? backupfiles.getTempFile() : chosenFile);
+            new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
 
-            if (doBackup)
-            {
+            if (doBackup) {
               backupfiles.setWriteSuccess(true);
               backupfiles.rollBackupsAndRenameTempFile();
             }
-          } catch (OutOfMemoryError oom)
-          {
-            new OOMWarning("Whilst saving current state to "
-                    + chosenFile.getName(), oom);
-          } catch (Exception ex)
-          {
-            Cache.log.error("Problems whilst trying to save to "
-                    + chosenFile.getName(), ex);
+          } catch (OutOfMemoryError oom) {
+            new OOMWarning("Whilst saving current state to " + chosenFile.getName(), oom);
+          } catch (Exception ex) {
+            Cache.log.error("Problems whilst trying to save to " + chosenFile.getName(), ex);
             JvOptionPane.showMessageDialog(me,
-                    MessageManager.formatMessage(
-                            "label.error_whilst_saving_current_state_to",
-                            new Object[]
-                            { chosenFile.getName() }),
-                    MessageManager.getString("label.couldnt_save_project"),
-                    JvOptionPane.WARNING_MESSAGE);
+                MessageManager.formatMessage("label.error_whilst_saving_current_state_to",
+                    new Object[] { chosenFile.getName() }),
+                MessageManager.getString("label.couldnt_save_project"), JvOptionPane.WARNING_MESSAGE);
           }
           setProgressBar(null, chosenFile.hashCode());
         }
@@ -1752,18 +1453,15 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   @Override
-  public void saveAsState_actionPerformed(ActionEvent e)
-  {
+  public void saveAsState_actionPerformed(ActionEvent e) {
     saveState_actionPerformed(true);
   }
 
-  private void setProjectFile(File choice)
-  {
+  private void setProjectFile(File choice) {
     this.projectFile = choice;
   }
 
-  public File getProjectFile()
-  {
+  public File getProjectFile() {
     return this.projectFile;
   }
 
@@ -1772,51 +1470,35 @@ public class Desktop extends jalview.jbgui.GDesktop
    * Jalview project
    */
   @Override
-  public void loadState_actionPerformed()
-  {
+  public void loadState_actionPerformed() {
     final String[] suffix = new String[] { "jvp", "jar" };
-    final String[] desc = new String[] { "Jalview Project",
-        "Jalview Project (old)" };
-    JalviewFileChooser chooser = new JalviewFileChooser(
-            Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
-            "Jalview Project", true, BackupFiles.getEnabled()); // last two
-                                                                // booleans:
-                                                                // allFiles,
+    final String[] desc = new String[] { "Jalview Project", "Jalview Project (old)" };
+    JalviewFileChooser chooser = new JalviewFileChooser(Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
+        "Jalview Project", true, BackupFiles.getEnabled()); // last two
+                                                            // booleans:
+                                                            // allFiles,
     // allowBackupFiles
     chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
-    chooser.setResponseHandler(0, new Runnable()
-    {
+    chooser.setResponseHandler(0, new Runnable() {
       @Override
-      public void run()
-      {
+      public void run() {
         File selectedFile = chooser.getSelectedFile();
         setProjectFile(selectedFile);
         String choice = selectedFile.getAbsolutePath();
         Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
-        new Thread(new Runnable()
-        {
+        new Thread(new Runnable() {
           @Override
-          public void run()
-          {
-            try
-            {
+          public void run() {
+            try {
               new Jalview2XML().loadJalviewAlign(selectedFile);
-            } catch (OutOfMemoryError oom)
-            {
+            } catch (OutOfMemoryError oom) {
               new OOMWarning("Whilst loading project from " + choice, oom);
-            } catch (Exception ex)
-            {
-              Cache.log.error(
-                      "Problems whilst loading project from " + choice, ex);
+            } catch (Exception ex) {
+              Cache.log.error("Problems whilst loading project from " + choice, ex);
               JvOptionPane.showMessageDialog(Desktop.desktop,
-                      MessageManager.formatMessage(
-                              "label.error_whilst_loading_project_from",
-                              new Object[]
-                              { choice }),
-                      MessageManager
-                              .getString("label.couldnt_load_project"),
-                      JvOptionPane.WARNING_MESSAGE);
+                  MessageManager.formatMessage("label.error_whilst_loading_project_from", new Object[] { choice }),
+                  MessageManager.getString("label.couldnt_load_project"), JvOptionPane.WARNING_MESSAGE);
             }
           }
         }, "Project Loader").start();
@@ -1827,8 +1509,7 @@ public class Desktop extends jalview.jbgui.GDesktop
   }
 
   @Override
-  public void inputSequence_actionPerformed(ActionEvent e)
-  {
+  public void inputSequence_actionPerformed(ActionEvent e) {
     new SequenceFetcher(this);
   }
 
@@ -1836,21 +1517,16 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
 
-  public void startLoading(final Object fileName)
-  {
-    if (fileLoadingCount == 0)
-    {
-      fileLoadingPanels.add(addProgressPanel(MessageManager
-              .formatMessage("label.loading_file", new Object[]
-              { fileName })));
+  public void startLoading(final Object fileName) {
+    if (fileLoadingCount == 0) {
+      fileLoadingPanels
+          .add(addProgressPanel(MessageManager.formatMessage("label.loading_file", new Object[] { fileName })));
     }
     fileLoadingCount++;
   }
 
-  private JPanel addProgressPanel(String string)
-  {
-    if (progressPanel == null)
-    {
+  private JPanel addProgressPanel(String string) {
+    if (progressPanel == null) {
       progressPanel = new JPanel(new GridLayout(1, 1));
       totalProgressCount = 0;
       instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
@@ -1863,8 +1539,7 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     thisprogress.add(progressBar, BorderLayout.CENTER);
     progressPanel.add(thisprogress);
-    ((GridLayout) progressPanel.getLayout()).setRows(
-            ((GridLayout) progressPanel.getLayout()).getRows() + 1);
+    ((GridLayout) progressPanel.getLayout()).setRows(((GridLayout) progressPanel.getLayout()).getRows() + 1);
     ++totalProgressCount;
     instance.validate();
     return thisprogress;
@@ -1872,17 +1547,13 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   int totalProgressCount = 0;
 
-  private void removeProgressPanel(JPanel progbar)
-  {
-    if (progressPanel != null)
-    {
-      synchronized (progressPanel)
-      {
+  private void removeProgressPanel(JPanel progbar) {
+    if (progressPanel != null) {
+      synchronized (progressPanel) {
         progressPanel.remove(progbar);
         GridLayout gl = (GridLayout) progressPanel.getLayout();
         gl.setRows(gl.getRows() - 1);
-        if (--totalProgressCount < 1)
-        {
+        if (--totalProgressCount < 1) {
           this.getContentPane().remove(progressPanel);
           progressPanel = null;
         }
@@ -1891,13 +1562,10 @@ public class Desktop extends jalview.jbgui.GDesktop
     validate();
   }
 
-  public void stopLoading()
-  {
+  public void stopLoading() {
     fileLoadingCount--;
-    if (fileLoadingCount < 1)
-    {
-      while (fileLoadingPanels.size() > 0)
-      {
+    if (fileLoadingCount < 1) {
+      while (fileLoadingPanels.size() > 0) {
         removeProgressPanel(fileLoadingPanels.remove(0));
       }
       fileLoadingPanels.clear();
@@ -1906,45 +1574,35 @@ public class Desktop extends jalview.jbgui.GDesktop
     validate();
   }
 
-  public static int getViewCount(String alignmentId)
-  {
+  public static int getViewCount(String alignmentId) {
     AlignmentViewport[] aps = getViewports(alignmentId);
     return (aps == null) ? 0 : aps.length;
   }
 
   /**
    * 
-   * @param alignmentId
-   *          - if null, all sets are returned
+   * @param alignmentId - if null, all sets are returned
    * @return all AlignmentPanels concerning the alignmentId sequence set
    */
-  public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
-  {
-    if (Desktop.desktop == null)
-    {
+  public static AlignmentPanel[] getAlignmentPanels(String alignmentId) {
+    if (Desktop.desktop == null) {
       // no frames created and in headless mode
       // TODO: verify that frames are recoverable when in headless mode
       return null;
     }
     List<AlignmentPanel> aps = new ArrayList<>();
     AlignFrame[] frames = getAlignFrames();
-    if (frames == null)
-    {
+    if (frames == null) {
       return null;
     }
-    for (AlignFrame af : frames)
-    {
-      for (AlignmentPanel ap : af.alignPanels)
-      {
-        if (alignmentId == null
-                || alignmentId.equals(ap.av.getSequenceSetId()))
-        {
+    for (AlignFrame af : frames) {
+      for (AlignmentPanel ap : af.alignPanels) {
+        if (alignmentId == null || alignmentId.equals(ap.av.getSequenceSetId())) {
           aps.add(ap);
         }
       }
     }
-    if (aps.size() == 0)
-    {
+    if (aps.size() == 0) {
       return null;
     }
     AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
@@ -1954,42 +1612,29 @@ public class Desktop extends jalview.jbgui.GDesktop
   /**
    * get all the viewports on an alignment.
    * 
-   * @param sequenceSetId
-   *          unique alignment id (may be null - all viewports returned in that
-   *          case)
+   * @param sequenceSetId unique alignment id (may be null - all viewports
+   *                      returned in that case)
    * @return all viewports on the alignment bound to sequenceSetId
    */
-  public static AlignmentViewport[] getViewports(String sequenceSetId)
-  {
+  public static AlignmentViewport[] getViewports(String sequenceSetId) {
     List<AlignmentViewport> viewp = new ArrayList<>();
-    if (desktop != null)
-    {
+    if (desktop != null) {
       AlignFrame[] frames = Desktop.getAlignFrames();
 
-      for (AlignFrame afr : frames)
-      {
-        if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
-                .equals(sequenceSetId))
-        {
-          if (afr.alignPanels != null)
-          {
-            for (AlignmentPanel ap : afr.alignPanels)
-            {
-              if (sequenceSetId == null
-                      || sequenceSetId.equals(ap.av.getSequenceSetId()))
-              {
+      for (AlignFrame afr : frames) {
+        if (sequenceSetId == null || afr.getViewport().getSequenceSetId().equals(sequenceSetId)) {
+          if (afr.alignPanels != null) {
+            for (AlignmentPanel ap : afr.alignPanels) {
+              if (sequenceSetId == null || sequenceSetId.equals(ap.av.getSequenceSetId())) {
                 viewp.add(ap.av);
               }
             }
-          }
-          else
-          {
+          } else {
             viewp.add(afr.getViewport());
           }
         }
       }
-      if (viewp.size() > 0)
-      {
+      if (viewp.size() > 0) {
         return viewp.toArray(new AlignmentViewport[viewp.size()]);
       }
     }
@@ -2001,56 +1646,47 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @param af
    */
-  public static void explodeViews(AlignFrame af)
-  {
+  public static void explodeViews(AlignFrame af) {
     int size = af.alignPanels.size();
-    if (size < 2)
-    {
+    if (size < 2) {
       return;
     }
 
     // FIXME: ideally should use UI interface API
-    FeatureSettings viewFeatureSettings = (af.featureSettings != null
-            && af.featureSettings.isOpen()) ? af.featureSettings : null;
+    FeatureSettings viewFeatureSettings = (af.featureSettings != null && af.featureSettings.isOpen())
+        ? af.featureSettings
+        : null;
     Rectangle fsBounds = af.getFeatureSettingsGeometry();
-    for (int i = 0; i < size; i++)
-    {
+    for (int i = 0; i < size; i++) {
       AlignmentPanel ap = af.alignPanels.get(i);
 
       AlignFrame newaf = new AlignFrame(ap);
 
       // transfer reference for existing feature settings to new alignFrame
-      if (ap == af.alignPanel)
-      {
-        if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
-        {
+      if (ap == af.alignPanel) {
+        if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap) {
           newaf.featureSettings = viewFeatureSettings;
         }
         newaf.setFeatureSettingsGeometry(fsBounds);
       }
 
       /*
-       * Restore the view's last exploded frame geometry if known. Multiple
-       * views from one exploded frame share and restore the same (frame)
-       * position and size.
+       * Restore the view's last exploded frame geometry if known. Multiple views from
+       * one exploded frame share and restore the same (frame) position and size.
        */
       Rectangle geometry = ap.av.getExplodedGeometry();
-      if (geometry != null)
-      {
+      if (geometry != null) {
         newaf.setBounds(geometry);
       }
 
       ap.av.setGatherViewsHere(false);
 
-      addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
-              AlignFrame.DEFAULT_HEIGHT);
+      addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
       // and materialise a new feature settings dialog instance for the new
       // alignframe
       // (closes the old as if 'OK' was pressed)
-      if (ap == af.alignPanel && newaf.featureSettings != null
-              && newaf.featureSettings.isOpen()
-              && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
-      {
+      if (ap == af.alignPanel && newaf.featureSettings != null && newaf.featureSettings.isOpen()
+          && af.alignPanel.getAlignViewport().isShowSequenceFeatures()) {
         newaf.showFeatureSettingsUI();
       }
     }
@@ -2063,29 +1699,24 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   /**
    * Gather expanded views (separate AlignFrame's) with the same sequence set
-   * identifier back in to this frame as additional views, and close the
-   * expanded views. Note the expanded frames may themselves have multiple
-   * views. We take the lot.
+   * identifier back in to this frame as additional views, and close the expanded
+   * views. Note the expanded frames may themselves have multiple views. We take
+   * the lot.
    * 
    * @param source
    */
-  public void gatherViews(AlignFrame source)
-  {
+  public void gatherViews(AlignFrame source) {
     source.viewport.setGatherViewsHere(true);
     source.viewport.setExplodedGeometry(source.getBounds());
     JInternalFrame[] frames = desktop.getAllFrames();
     String viewId = source.viewport.getSequenceSetId();
-    for (int t = 0; t < frames.length; t++)
-    {
-      if (frames[t] instanceof AlignFrame && frames[t] != source)
-      {
+    for (int t = 0; t < frames.length; t++) {
+      if (frames[t] instanceof AlignFrame && frames[t] != source) {
         AlignFrame af = (AlignFrame) frames[t];
         boolean gatherThis = false;
-        for (int a = 0; a < af.alignPanels.size(); a++)
-        {
+        for (int a = 0; a < af.alignPanels.size(); a++) {
           AlignmentPanel ap = af.alignPanels.get(a);
-          if (viewId.equals(ap.av.getSequenceSetId()))
-          {
+          if (viewId.equals(ap.av.getSequenceSetId())) {
             gatherThis = true;
             ap.av.setGatherViewsHere(false);
             ap.av.setExplodedGeometry(af.getBounds());
@@ -2093,19 +1724,13 @@ public class Desktop extends jalview.jbgui.GDesktop
           }
         }
 
-        if (gatherThis)
-        {
-          if (af.featureSettings != null && af.featureSettings.isOpen())
-          {
-            if (source.featureSettings == null)
-            {
+        if (gatherThis) {
+          if (af.featureSettings != null && af.featureSettings.isOpen()) {
+            if (source.featureSettings == null) {
               // preserve the feature settings geometry for this frame
               source.featureSettings = af.featureSettings;
-              source.setFeatureSettingsGeometry(
-                      af.getFeatureSettingsGeometry());
-            }
-            else
-            {
+              source.setFeatureSettingsGeometry(af.getFeatureSettingsGeometry());
+            } else {
               // close it and forget
               af.featureSettings.close();
             }
@@ -2117,14 +1742,13 @@ public class Desktop extends jalview.jbgui.GDesktop
     }
 
     // refresh the feature setting UI for the source frame if it exists
-    if (source.featureSettings != null && source.featureSettings.isOpen())
-    {
+    if (source.featureSettings != null && source.featureSettings.isOpen()) {
       source.showFeatureSettingsUI();
     }
+
   }
 
-  public JInternalFrame[] getAllFrames()
-  {
+  public JInternalFrame[] getAllFrames() {
     return desktop.getAllFrames();
   }
 
@@ -2134,49 +1758,37 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @param url
    */
-  public void checkForQuestionnaire(String url)
-  {
+  public void checkForQuestionnaire(String url) {
     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
     // javax.swing.SwingUtilities.invokeLater(jvq);
     new Thread(jvq).start();
   }
 
-  public void checkURLLinks()
-  {
+  public void checkURLLinks() {
     // Thread off the URL link checker
-    addDialogThread(new Runnable()
-    {
+    addDialogThread(new Runnable() {
       @Override
-      public void run()
-      {
-        if (Cache.getDefault("CHECKURLLINKS", true))
-        {
+      public void run() {
+        if (Cache.getDefault("CHECKURLLINKS", true)) {
           // check what the actual links are - if it's just the default don't
           // bother with the warning
-          List<String> links = Preferences.sequenceUrlLinks
-                  .getLinksForMenu();
+          List<String> links = Preferences.sequenceUrlLinks.getLinksForMenu();
 
           // only need to check links if there is one with a
           // SEQUENCE_ID which is not the default EMBL_EBI link
           ListIterator<String> li = links.listIterator();
           boolean check = false;
           List<JLabel> urls = new ArrayList<>();
-          while (li.hasNext())
-          {
+          while (li.hasNext()) {
             String link = li.next();
-            if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
-                    && !UrlConstants.isDefaultString(link))
-            {
+            if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID) && !UrlConstants.isDefaultString(link)) {
               check = true;
               int barPos = link.indexOf("|");
-              String urlMsg = barPos == -1 ? link
-                      : link.substring(0, barPos) + ": "
-                              + link.substring(barPos + 1);
+              String urlMsg = barPos == -1 ? link : link.substring(0, barPos) + ": " + link.substring(barPos + 1);
               urls.add(new JLabel(urlMsg));
             }
           }
-          if (!check)
-          {
+          if (!check) {
             return;
           }
 
@@ -2185,36 +1797,27 @@ public class Desktop extends jalview.jbgui.GDesktop
           JPanel msgPanel = new JPanel();
           msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
           msgPanel.add(Box.createVerticalGlue());
-          JLabel msg = new JLabel(MessageManager
-                  .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
-          JLabel msg2 = new JLabel(MessageManager
-                  .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
+          JLabel msg = new JLabel(MessageManager.getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
+          JLabel msg2 = new JLabel(MessageManager.getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
           msgPanel.add(msg);
-          for (JLabel url : urls)
-          {
+          for (JLabel url : urls) {
             msgPanel.add(url);
           }
           msgPanel.add(msg2);
 
-          final JCheckBox jcb = new JCheckBox(
-                  MessageManager.getString("label.do_not_display_again"));
-          jcb.addActionListener(new ActionListener()
-          {
+          final JCheckBox jcb = new JCheckBox(MessageManager.getString("label.do_not_display_again"));
+          jcb.addActionListener(new ActionListener() {
             @Override
-            public void actionPerformed(ActionEvent e)
-            {
+            public void actionPerformed(ActionEvent e) {
               // update Cache settings for "don't show this again"
               boolean showWarningAgain = !jcb.isSelected();
-              Cache.setProperty("CHECKURLLINKS",
-                      Boolean.valueOf(showWarningAgain).toString());
+              Cache.setProperty("CHECKURLLINKS", Boolean.valueOf(showWarningAgain).toString());
             }
           });
           msgPanel.add(jcb);
 
           JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
-                  MessageManager
-                          .getString("label.SEQUENCE_ID_no_longer_used"),
-                  JvOptionPane.WARNING_MESSAGE);
+              MessageManager.getString("label.SEQUENCE_ID_no_longer_used"), JvOptionPane.WARNING_MESSAGE);
         }
       }
     });
@@ -2222,13 +1825,11 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   /**
    * Proxy class for JDesktopPane which optionally displays the current memory
-   * usage and highlights the desktop area with a red bar if free memory runs
-   * low.
+   * usage and highlights the desktop area with a red bar if free memory runs low.
    * 
    * @author AMW
    */
-  public class MyDesktopPane extends JDesktopPane implements Runnable
-  {
+  public class MyDesktopPane extends JDesktopPane implements Runnable {
     private static final float ONE_MB = 1048576f;
 
     boolean showMemoryUsage = false;
@@ -2237,41 +1838,33 @@ public class Desktop extends jalview.jbgui.GDesktop
 
     java.text.NumberFormat df;
 
-    float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
-            percentUsage;
+    float maxMemory, allocatedMemory, freeMemory, totalFreeMemory, percentUsage;
 
-    public MyDesktopPane(boolean showMemoryUsage)
-    {
+    public MyDesktopPane(boolean showMemoryUsage) {
       showMemoryUsage(showMemoryUsage);
     }
 
-    public void showMemoryUsage(boolean showMemory)
-    {
+    public void showMemoryUsage(boolean showMemory) {
       this.showMemoryUsage = showMemory;
-      if (showMemory)
-      {
+      if (showMemory) {
         Thread worker = new Thread(this);
         worker.start();
       }
       repaint();
     }
 
-    public boolean isShowMemoryUsage()
-    {
+    public boolean isShowMemoryUsage() {
       return showMemoryUsage;
     }
 
     @Override
-    public void run()
-    {
+    public void run() {
       df = java.text.NumberFormat.getNumberInstance();
       df.setMaximumFractionDigits(2);
       runtime = Runtime.getRuntime();
 
-      while (showMemoryUsage)
-      {
-        try
-        {
+      while (showMemoryUsage) {
+        try {
           maxMemory = runtime.maxMemory() / ONE_MB;
           allocatedMemory = runtime.totalMemory() / ONE_MB;
           freeMemory = runtime.freeMemory() / ONE_MB;
@@ -2288,30 +1881,24 @@ public class Desktop extends jalview.jbgui.GDesktop
           repaint();
           // sleep after showing usage
           Thread.sleep(3000);
-        } catch (Exception ex)
-        {
+        } catch (Exception ex) {
           ex.printStackTrace();
         }
       }
     }
 
     @Override
-    public void paintComponent(Graphics g)
-    {
-      if (showMemoryUsage && g != null && df != null)
-      {
-        if (percentUsage < 20)
-        {
+    public void paintComponent(Graphics g) {
+      if (showMemoryUsage && g != null && df != null) {
+        if (percentUsage < 20) {
           g.setColor(Color.red);
         }
         FontMetrics fm = g.getFontMetrics();
-        if (fm != null)
-        {
-          g.drawString(MessageManager.formatMessage("label.memory_stats",
-                  new Object[]
-                  { df.format(totalFreeMemory), df.format(maxMemory),
-                      df.format(percentUsage) }),
-                  10, getHeight() - fm.getHeight());
+        if (fm != null) {
+          g.drawString(
+              MessageManager.formatMessage("label.memory_stats",
+                  new Object[] { df.format(totalFreeMemory), df.format(maxMemory), df.format(percentUsage) }),
+              10, getHeight() - fm.getHeight());
         }
       }
 
@@ -2325,46 +1912,36 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @return an array of AlignFrame, or null if none found
    */
-  public static AlignFrame[] getAlignFrames()
-  {
-    if (Jalview.isHeadlessMode())
-    {
+  public static AlignFrame[] getAlignFrames() {
+    if (Jalview.isHeadlessMode()) {
       // Desktop.desktop is null in headless mode
       return new AlignFrame[] { Jalview.currentAlignFrame };
     }
 
     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
 
-    if (frames == null)
-    {
+    if (frames == null) {
       return null;
     }
     List<AlignFrame> avp = new ArrayList<>();
     // REVERSE ORDER
-    for (int i = frames.length - 1; i > -1; i--)
-    {
-      if (frames[i] instanceof AlignFrame)
-      {
+    for (int i = frames.length - 1; i > -1; i--) {
+      if (frames[i] instanceof AlignFrame) {
         avp.add((AlignFrame) frames[i]);
-      }
-      else if (frames[i] instanceof SplitFrame)
-      {
+      } else if (frames[i] instanceof SplitFrame) {
         /*
          * Also check for a split frame containing an AlignFrame
          */
         GSplitFrame sf = (GSplitFrame) frames[i];
-        if (sf.getTopFrame() instanceof AlignFrame)
-        {
+        if (sf.getTopFrame() instanceof AlignFrame) {
           avp.add((AlignFrame) sf.getTopFrame());
         }
-        if (sf.getBottomFrame() instanceof AlignFrame)
-        {
+        if (sf.getBottomFrame() instanceof AlignFrame) {
           avp.add((AlignFrame) sf.getBottomFrame());
         }
       }
     }
-    if (avp.size() == 0)
-    {
+    if (avp.size() == 0) {
       return null;
     }
     AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
@@ -2376,26 +1953,21 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @return
    */
-  public GStructureViewer[] getJmols()
-  {
+  public GStructureViewer[] getJmols() {
     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
 
-    if (frames == null)
-    {
+    if (frames == null) {
       return null;
     }
     List<GStructureViewer> avp = new ArrayList<>();
     // REVERSE ORDER
-    for (int i = frames.length - 1; i > -1; i--)
-    {
-      if (frames[i] instanceof AppJmol)
-      {
+    for (int i = frames.length - 1; i > -1; i--) {
+      if (frames[i] instanceof AppJmol) {
         GStructureViewer af = (GStructureViewer) frames[i];
         avp.add(af);
       }
     }
-    if (avp.size() == 0)
-    {
+    if (avp.size() == 0) {
       return null;
     }
     GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
@@ -2406,45 +1978,37 @@ public class Desktop extends jalview.jbgui.GDesktop
    * Add Groovy Support to Jalview
    */
   @Override
-  public void groovyShell_actionPerformed()
-  {
-    try
-    {
+  public void groovyShell_actionPerformed() {
+    try {
       openGroovyConsole();
-    } catch (Exception ex)
-    {
+    } catch (Exception ex) {
       Cache.log.error("Groovy Shell Creation failed.", ex);
       JvOptionPane.showInternalMessageDialog(Desktop.desktop,
 
-              MessageManager.getString("label.couldnt_create_groovy_shell"),
-              MessageManager.getString("label.groovy_support_failed"),
-              JvOptionPane.ERROR_MESSAGE);
+          MessageManager.getString("label.couldnt_create_groovy_shell"),
+          MessageManager.getString("label.groovy_support_failed"), JvOptionPane.ERROR_MESSAGE);
     }
   }
 
   /**
    * Open the Groovy console
    */
-  void openGroovyConsole()
-  {
-    if (groovyConsole == null)
-    {
+  void openGroovyConsole() {
+    if (groovyConsole == null) {
       groovyConsole = new groovy.ui.Console();
       groovyConsole.setVariable("Jalview", this);
       groovyConsole.run();
 
       /*
        * We allow only one console at a time, so that AlignFrame menu option
-       * 'Calculate | Run Groovy script' is unambiguous.
-       * Disable 'Groovy Console', and enable 'Run script', when the console is 
-       * opened, and the reverse when it is closed
+       * 'Calculate | Run Groovy script' is unambiguous. Disable 'Groovy Console', and
+       * enable 'Run script', when the console is opened, and the reverse when it is
+       * closed
        */
       Window window = (Window) groovyConsole.getFrame();
-      window.addWindowListener(new WindowAdapter()
-      {
+      window.addWindowListener(new WindowAdapter() {
         @Override
-        public void windowClosed(WindowEvent e)
-        {
+        public void windowClosed(WindowEvent e) {
           /*
            * rebind CMD-Q from Groovy Console to Jalview Quit
            */
@@ -2460,30 +2024,23 @@ public class Desktop extends jalview.jbgui.GDesktop
     ((Window) groovyConsole.getFrame()).setVisible(true);
 
     /*
-     * if we got this far, enable 'Run Groovy' in AlignFrame menus
-     * and disable opening a second console
+     * if we got this far, enable 'Run Groovy' in AlignFrame menus and disable
+     * opening a second console
      */
     enableExecuteGroovy(true);
   }
 
   /**
-   * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
-   * binding when opened
-   */
-  protected void addQuitHandler()
-  {
-    getRootPane()
-            .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-                    KeyStroke
-                            .getKeyStroke(KeyEvent.VK_Q,
-                                    jalview.util.ShortcutKeyMaskExWrapper
-                                            .getMenuShortcutKeyMaskEx()),
-                    "Quit");
-    getRootPane().getActionMap().put("Quit", new AbstractAction()
-    {
+   * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
+   * when opened
+   */
+  protected void addQuitHandler() {
+    getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
+        KeyStroke.getKeyStroke(KeyEvent.VK_Q, jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
+        "Quit");
+    getRootPane().getActionMap().put("Quit", new AbstractAction() {
       @Override
-      public void actionPerformed(ActionEvent e)
-      {
+      public void actionPerformed(ActionEvent e) {
         quit();
       }
     });
@@ -2492,22 +2049,18 @@ public class Desktop extends jalview.jbgui.GDesktop
   /**
    * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
    * 
-   * @param enabled
-   *          true if Groovy console is open
+   * @param enabled true if Groovy console is open
    */
-  public void enableExecuteGroovy(boolean enabled)
-  {
+  public void enableExecuteGroovy(boolean enabled) {
     /*
-     * disable opening a second Groovy console
-     * (or re-enable when the console is closed)
+     * disable opening a second Groovy console (or re-enable when the console is
+     * closed)
      */
     groovyShell.setEnabled(!enabled);
 
     AlignFrame[] alignFrames = getAlignFrames();
-    if (alignFrames != null)
-    {
-      for (AlignFrame af : alignFrames)
-      {
+    if (alignFrames != null) {
+      for (AlignFrame af : alignFrames) {
         af.setGroovyEnabled(enabled);
       }
     }
@@ -2526,27 +2079,19 @@ public class Desktop extends jalview.jbgui.GDesktop
    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
    */
   @Override
-  public void setProgressBar(String message, long id)
-  {
-    // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
-
-    if (progressBars == null)
-    {
+  public void setProgressBar(String message, long id) {
+    if (progressBars == null) {
       progressBars = new Hashtable<>();
       progressBarHandlers = new Hashtable<>();
     }
 
-    if (progressBars.get(Long.valueOf(id)) != null)
-    {
+    if (progressBars.get(Long.valueOf(id)) != null) {
       JPanel panel = progressBars.remove(Long.valueOf(id));
-      if (progressBarHandlers.contains(Long.valueOf(id)))
-      {
+      if (progressBarHandlers.contains(Long.valueOf(id))) {
         progressBarHandlers.remove(Long.valueOf(id));
       }
       removeProgressPanel(panel);
-    }
-    else
-    {
+    } else {
       progressBars.put(Long.valueOf(id), addProgressPanel(message));
     }
   }
@@ -2558,33 +2103,22 @@ public class Desktop extends jalview.jbgui.GDesktop
    * jalview.gui.IProgressIndicatorHandler)
    */
   @Override
-  public void registerHandler(final long id,
-          final IProgressIndicatorHandler handler)
-  {
-    if (progressBarHandlers == null
-            || !progressBars.containsKey(Long.valueOf(id)))
-    {
-      throw new Error(MessageManager.getString(
-              "error.call_setprogressbar_before_registering_handler"));
+  public void registerHandler(final long id, final IProgressIndicatorHandler handler) {
+    if (progressBarHandlers == null || !progressBars.containsKey(Long.valueOf(id))) {
+      throw new Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
     }
     progressBarHandlers.put(Long.valueOf(id), handler);
     final JPanel progressPanel = progressBars.get(Long.valueOf(id));
-    if (handler.canCancel())
-    {
-      JButton cancel = new JButton(
-              MessageManager.getString("action.cancel"));
+    if (handler.canCancel()) {
+      JButton cancel = new JButton(MessageManager.getString("action.cancel"));
       final IProgressIndicator us = this;
-      cancel.addActionListener(new ActionListener()
-      {
+      cancel.addActionListener(new ActionListener() {
 
         @Override
-        public void actionPerformed(ActionEvent e)
-        {
+        public void actionPerformed(ActionEvent e) {
           handler.cancelActivity(id);
-          us.setProgressBar(MessageManager
-                  .formatMessage("label.cancelled_params", new Object[]
-                  { ((JLabel) progressPanel.getComponent(0)).getText() }),
-                  id);
+          us.setProgressBar(MessageManager.formatMessage("label.cancelled_params",
+              new Object[] { ((JLabel) progressPanel.getComponent(0)).getText() }), id);
         }
       });
       progressPanel.add(cancel, BorderLayout.EAST);
@@ -2596,33 +2130,25 @@ public class Desktop extends jalview.jbgui.GDesktop
    * @return true if any progress bars are still active
    */
   @Override
-  public boolean operationInProgress()
-  {
-    if (progressBars != null && progressBars.size() > 0)
-    {
+  public boolean operationInProgress() {
+    if (progressBars != null && progressBars.size() > 0) {
       return true;
     }
     return false;
   }
 
   /**
-   * This will return the first AlignFrame holding the given viewport instance.
-   * It will break if there are more than one AlignFrames viewing a particular
-   * av.
+   * This will return the first AlignFrame holding the given viewport instance. It
+   * will break if there are more than one AlignFrames viewing a particular av.
    * 
    * @param viewport
    * @return alignFrame for viewport
    */
-  public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
-  {
-    if (desktop != null)
-    {
-      AlignmentPanel[] aps = getAlignmentPanels(
-              viewport.getSequenceSetId());
-      for (int panel = 0; aps != null && panel < aps.length; panel++)
-      {
-        if (aps[panel] != null && aps[panel].av == viewport)
-        {
+  public static AlignFrame getAlignFrameFor(AlignViewportI viewport) {
+    if (desktop != null) {
+      AlignmentPanel[] aps = getAlignmentPanels(viewport.getSequenceSetId());
+      for (int panel = 0; aps != null && panel < aps.length; panel++) {
+        if (aps[panel] != null && aps[panel].av == viewport) {
           return aps[panel].alignFrame;
         }
       }
@@ -2630,8 +2156,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     return null;
   }
 
-  public VamsasApplication getVamsasApplication()
-  {
+  public VamsasApplication getVamsasApplication() {
     // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
     return null;
 
@@ -2647,8 +2172,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @return inBatchMode
    */
-  public boolean isInBatchMode()
-  {
+  public boolean isInBatchMode() {
     return inBatchMode;
   }
 
@@ -2657,26 +2181,42 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @param inBatchMode
    */
-  public void setInBatchMode(boolean inBatchMode)
-  {
+  public void setInBatchMode(boolean inBatchMode) {
     this.inBatchMode = inBatchMode;
   }
 
-  public void startServiceDiscovery()
-  {
+  /**
+   * start service discovery and wait till it is done
+   */
+  public void startServiceDiscovery() {
     startServiceDiscovery(false);
   }
 
-  public void startServiceDiscovery(boolean blocking)
-  {
+  /**
+   * start service discovery threads - blocking or non-blocking
+   * 
+   * @param blocking
+   */
+  public void startServiceDiscovery(boolean blocking) {
+    startServiceDiscovery(blocking, false);
+  }
+
+  /**
+   * start service discovery threads
+   * 
+   * @param blocking                             - false means call returns
+   *                                             immediately
+   * @param ignore_SHOW_JWS2_SERVICES_preference - when true JABA services are
+   *                                             discovered regardless of user's
+   *                                             JWS2 discovery preference setting
+   */
+  public void startServiceDiscovery(boolean blocking, boolean ignore_SHOW_JWS2_SERVICES_preference) {
     boolean alive = true;
     Thread t0 = null, t1 = null, t2 = null;
     // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
-    if (true)
-    {
+    if (true) {
       // todo: changesupport handlers need to be transferred
-      if (discoverer == null)
-      {
+      if (discoverer == null) {
         discoverer = new jalview.ws.jws1.Discoverer();
         // register PCS handler for desktop.
         discoverer.addPropertyChangeListener(changeSupport);
@@ -2686,28 +2226,21 @@ public class Desktop extends jalview.jbgui.GDesktop
       (t0 = new Thread(discoverer)).start();
     }
 
-    if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
-    {
-      t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
-              .startDiscoverer(changeSupport);
+    if (ignore_SHOW_JWS2_SERVICES_preference || Cache.getDefault("SHOW_JWS2_SERVICES", true)) {
+      t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer(changeSupport);
     }
     Thread t3 = null;
     {
       // TODO: do rest service discovery
     }
-    if (blocking)
-    {
-      while (alive)
-      {
-        try
-        {
+    if (blocking) {
+      while (alive) {
+        try {
           Thread.sleep(15);
-        } catch (Exception e)
-        {
+        } catch (Exception e) {
         }
-        alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
-                || (t3 != null && t3.isAlive())
-                || (t0 != null && t0.isAlive());
+        alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive()) || (t3 != null && t3.isAlive())
+            || (t0 != null && t0.isAlive());
       }
     }
   }
@@ -2717,70 +2250,50 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @param evt
    */
-  protected void JalviewServicesChanged(PropertyChangeEvent evt)
-  {
-    if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
-    {
-      final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
-              .getErrorMessages();
-      if (ermsg != null)
-      {
-        if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
-        {
-          if (serviceChangedDialog == null)
-          {
+  protected void JalviewServicesChanged(PropertyChangeEvent evt) {
+    if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector) {
+      final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer().getErrorMessages();
+      if (ermsg != null) {
+        if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true)) {
+          if (serviceChangedDialog == null) {
             // only run if we aren't already displaying one of these.
-            addDialogThread(serviceChangedDialog = new Runnable()
-            {
+            addDialogThread(serviceChangedDialog = new Runnable() {
               @Override
-              public void run()
-              {
+              public void run() {
 
                 /*
                  * JalviewDialog jd =new JalviewDialog() {
                  * 
-                 * @Override protected void cancelPressed() { // TODO
-                 * Auto-generated method stub
+                 * @Override protected void cancelPressed() { // TODO Auto-generated method stub
                  * 
-                 * }@Override protected void okPressed() { // TODO
-                 * Auto-generated method stub
+                 * }@Override protected void okPressed() { // TODO Auto-generated method stub
                  * 
-                 * }@Override protected void raiseClosed() { // TODO
-                 * Auto-generated method stub
+                 * }@Override protected void raiseClosed() { // TODO Auto-generated method stub
                  * 
-                 * } }; jd.initDialogFrame(new
-                 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
+                 * } }; jd.initDialogFrame(new JLabel("<html><table width=\"450\"><tr><td>" +
+                 * ermsg +
                  * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
                  * + " or mis-configured HTTP proxy settings.<br/>" +
-                 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
-                 * +
-                 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
-                 * ), true, true, "Web Service Configuration Problem", 450,
-                 * 400);
+                 * "Check the <em>Connections</em> and <em>Web services</em> tab of the" +
+                 * " Tools->Preferences dialog box to change them.</td></tr></table></html>" ),
+                 * true, true, "Web Service Configuration Problem", 450, 400);
                  * 
                  * jd.waitForInput();
                  */
                 JvOptionPane.showConfirmDialog(Desktop.desktop,
-                        new JLabel("<html><table width=\"450\"><tr><td>"
-                                + ermsg + "</td></tr></table>"
-                                + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
-                                + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
-                                + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
-                                + " Tools->Preferences dialog box to change them.</p></html>"),
-                        "Web Service Configuration Problem",
-                        JvOptionPane.DEFAULT_OPTION,
-                        JvOptionPane.ERROR_MESSAGE);
+                    new JLabel("<html><table width=\"450\"><tr><td>" + ermsg + "</td></tr></table>"
+                        + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
+                        + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
+                        + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
+                        + " Tools->Preferences dialog box to change them.</p></html>"),
+                    "Web Service Configuration Problem", JvOptionPane.DEFAULT_OPTION, JvOptionPane.ERROR_MESSAGE);
                 serviceChangedDialog = null;
 
               }
             });
           }
-        }
-        else
-        {
-          Cache.log.error(
-                  "Errors reported by JABA discovery service. Check web services preferences.\n"
-                          + ermsg);
+        } else {
+          Cache.log.error("Errors reported by JABA discovery service. Check web services preferences.\n" + ermsg);
         }
       }
     }
@@ -2795,8 +2308,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @param url
    */
-  public static void showUrl(final String url)
-  {
+  public static void showUrl(final String url) {
     showUrl(url, Desktop.instance);
   }
 
@@ -2804,38 +2316,26 @@ public class Desktop extends jalview.jbgui.GDesktop
    * Like showUrl but allows progress handler to be specified
    * 
    * @param url
-   * @param progress
-   *          (null) or object implementing IProgressIndicator
+   * @param progress (null) or object implementing IProgressIndicator
    */
-  public static void showUrl(final String url,
-          final IProgressIndicator progress)
-  {
-    new Thread(new Runnable()
-    {
+  public static void showUrl(final String url, final IProgressIndicator progress) {
+    new Thread(new Runnable() {
       @Override
-      public void run()
-      {
-        try
-        {
-          if (progress != null)
-          {
-            progress.setProgressBar(MessageManager
-                    .formatMessage("status.opening_params", new Object[]
-                    { url }), this.hashCode());
+      public void run() {
+        try {
+          if (progress != null) {
+            progress.setProgressBar(MessageManager.formatMessage("status.opening_params", new Object[] { url }),
+                this.hashCode());
           }
           jalview.util.BrowserLauncher.openURL(url);
-        } catch (Exception ex)
-        {
+        } catch (Exception ex) {
           JvOptionPane.showInternalMessageDialog(Desktop.desktop,
-                  MessageManager
-                          .getString("label.web_browser_not_found_unix"),
-                  MessageManager.getString("label.web_browser_not_found"),
-                  JvOptionPane.WARNING_MESSAGE);
+              MessageManager.getString("label.web_browser_not_found_unix"),
+              MessageManager.getString("label.web_browser_not_found"), JvOptionPane.WARNING_MESSAGE);
 
           ex.printStackTrace();
         }
-        if (progress != null)
-        {
+        if (progress != null) {
           progress.setProgressBar(null, this.hashCode());
         }
       }
@@ -2844,10 +2344,8 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   public static WsParamSetManager wsparamManager = null;
 
-  public static ParamManager getUserParameterStore()
-  {
-    if (wsparamManager == null)
-    {
+  public static ParamManager getUserParameterStore() {
+    if (wsparamManager == null) {
       wsparamManager = new WsParamSetManager();
     }
     return wsparamManager;
@@ -2858,27 +2356,18 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @param e
    */
-  public static void hyperlinkUpdate(HyperlinkEvent e)
-  {
-    if (e.getEventType() == EventType.ACTIVATED)
-    {
+  public static void hyperlinkUpdate(HyperlinkEvent e) {
+    if (e.getEventType() == EventType.ACTIVATED) {
       String url = null;
-      try
-      {
+      try {
         url = e.getURL().toString();
         Desktop.showUrl(url);
-      } catch (Exception x)
-      {
-        if (url != null)
-        {
-          if (Cache.log != null)
-          {
+      } catch (Exception x) {
+        if (url != null) {
+          if (Cache.log != null) {
             Cache.log.error("Couldn't handle string " + url + " as a URL.");
-          }
-          else
-          {
-            System.err.println(
-                    "Couldn't handle string " + url + " as a URL.");
+          } else {
+            System.err.println("Couldn't handle string " + url + " as a URL.");
           }
         }
         // ignore any exceptions due to dud links.
@@ -2909,39 +2398,29 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @param prompter
    */
-  public void addDialogThread(final Runnable prompter)
-  {
-    dialogExecutor.submit(new Runnable()
-    {
+  public void addDialogThread(final Runnable prompter) {
+    dialogExecutor.submit(new Runnable() {
       @Override
-      public void run()
-      {
-        if (dialogPause)
-        {
-          try
-          {
+      public void run() {
+        if (dialogPause) {
+          try {
             block.acquire();
-          } catch (InterruptedException x)
-          {
+          } catch (InterruptedException x) {
           }
         }
-        if (instance == null)
-        {
+        if (instance == null) {
           return;
         }
-        try
-        {
+        try {
           SwingUtilities.invokeAndWait(prompter);
-        } catch (Exception q)
-        {
+        } catch (Exception q) {
           Cache.log.warn("Unexpected Exception in dialog thread.", q);
         }
       }
     });
   }
 
-  public void startDialogQueue()
-  {
+  public void startDialogQueue() {
     // set the flag so we don't pause waiting for another permit and semaphore
     // the current task to begin
     dialogPause = false;
@@ -2958,65 +2437,53 @@ public class Desktop extends jalview.jbgui.GDesktop
    * </pre>
    */
   @Override
-  protected void snapShotWindow_actionPerformed(ActionEvent e)
-  {
+  protected void snapShotWindow_actionPerformed(ActionEvent e) {
     // currently the menu option to do this is not shown
     invalidate();
 
     int width = getWidth();
     int height = getHeight();
-    File of = new File(
-            "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
-    ImageWriterI writer = new ImageWriterI()
-    {
+    File of = new File("Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
+    ImageWriterI writer = new ImageWriterI() {
       @Override
-      public void exportImage(Graphics g) throws Exception
-      {
+      public void exportImage(Graphics g) throws Exception {
         paintAll(g);
-        Cache.log.info("Successfully written snapshot to file "
-                + of.getAbsolutePath());
+        Cache.log.info("Successfully written snapshot to file " + of.getAbsolutePath());
       }
     };
     String title = "View of desktop";
-    ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
-            title);
+    ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS, title);
     exporter.doExport(of, this, width, height, title);
   }
 
   /**
    * Explode the views in the given SplitFrame into separate SplitFrame windows.
-   * This respects (remembers) any previous 'exploded geometry' i.e. the size
-   * and location last time the view was expanded (if any). However it does not
+   * This respects (remembers) any previous 'exploded geometry' i.e. the size and
+   * location last time the view was expanded (if any). However it does not
    * remember the split pane divider location - this is set to match the
    * 'exploding' frame.
    * 
    * @param sf
    */
-  public void explodeViews(SplitFrame sf)
-  {
+  public void explodeViews(SplitFrame sf) {
     AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
     AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
-    List<? extends AlignmentViewPanel> topPanels = oldTopFrame
-            .getAlignPanels();
-    List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
-            .getAlignPanels();
+    List<? extends AlignmentViewPanel> topPanels = oldTopFrame.getAlignPanels();
+    List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame.getAlignPanels();
     int viewCount = topPanels.size();
-    if (viewCount < 2)
-    {
+    if (viewCount < 2) {
       return;
     }
 
     /*
-     * Processing in reverse order works, forwards order leaves the first panels
-     * not visible. I don't know why!
+     * Processing in reverse order works, forwards order leaves the first panels not
+     * visible. I don't know why!
      */
-    for (int i = viewCount - 1; i >= 0; i--)
-    {
+    for (int i = viewCount - 1; i >= 0; i--) {
       /*
-       * Make new top and bottom frames. These take over the respective
-       * AlignmentPanel objects, including their AlignmentViewports, so the
-       * cdna/protein relationships between the viewports is carried over to the
-       * new split frames.
+       * Make new top and bottom frames. These take over the respective AlignmentPanel
+       * objects, including their AlignmentViewports, so the cdna/protein
+       * relationships between the viewports is carried over to the new split frames.
        * 
        * explodedGeometry holds the (x, y) position of the previously exploded
        * SplitFrame, and the (width, height) of the AlignFrame component
@@ -3025,10 +2492,8 @@ public class Desktop extends jalview.jbgui.GDesktop
       AlignFrame newTopFrame = new AlignFrame(topPanel);
       newTopFrame.setSize(oldTopFrame.getSize());
       newTopFrame.setVisible(true);
-      Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
-              .getExplodedGeometry();
-      if (geometry != null)
-      {
+      Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport()).getExplodedGeometry();
+      if (geometry != null) {
         newTopFrame.setSize(geometry.getSize());
       }
 
@@ -3036,27 +2501,23 @@ public class Desktop extends jalview.jbgui.GDesktop
       AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
       newBottomFrame.setSize(oldBottomFrame.getSize());
       newBottomFrame.setVisible(true);
-      geometry = ((AlignViewport) bottomPanel.getAlignViewport())
-              .getExplodedGeometry();
-      if (geometry != null)
-      {
+      geometry = ((AlignViewport) bottomPanel.getAlignViewport()).getExplodedGeometry();
+      if (geometry != null) {
         newBottomFrame.setSize(geometry.getSize());
       }
 
       topPanel.av.setGatherViewsHere(false);
       bottomPanel.av.setGatherViewsHere(false);
-      JInternalFrame splitFrame = new SplitFrame(newTopFrame,
-              newBottomFrame);
-      if (geometry != null)
-      {
+      JInternalFrame splitFrame = new SplitFrame(newTopFrame, newBottomFrame);
+      if (geometry != null) {
         splitFrame.setLocation(geometry.getLocation());
       }
       Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
     }
 
     /*
-     * Clear references to the panels (now relocated in the new SplitFrames)
-     * before closing the old SplitFrame.
+     * Clear references to the panels (now relocated in the new SplitFrames) before
+     * closing the old SplitFrame.
      */
     topPanels.clear();
     bottomPanels.clear();
@@ -3070,8 +2531,7 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * @param source
    */
-  public void gatherViews(GSplitFrame source)
-  {
+  public void gatherViews(GSplitFrame source) {
     /*
      * special handling of explodedGeometry for a view within a SplitFrame: - it
      * holds the (x, y) position of the enclosing SplitFrame, and the (width,
@@ -3079,46 +2539,38 @@ public class Desktop extends jalview.jbgui.GDesktop
      */
     AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
     AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
-    myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
-            source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
-    myBottomFrame.viewport
-            .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
-                    myBottomFrame.getWidth(), myBottomFrame.getHeight()));
+    myTopFrame.viewport.setExplodedGeometry(
+        new Rectangle(source.getX(), source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
+    myBottomFrame.viewport.setExplodedGeometry(
+        new Rectangle(source.getX(), source.getY(), myBottomFrame.getWidth(), myBottomFrame.getHeight()));
     myTopFrame.viewport.setGatherViewsHere(true);
     myBottomFrame.viewport.setGatherViewsHere(true);
     String topViewId = myTopFrame.viewport.getSequenceSetId();
     String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
 
     JInternalFrame[] frames = desktop.getAllFrames();
-    for (JInternalFrame frame : frames)
-    {
-      if (frame instanceof SplitFrame && frame != source)
-      {
+    for (JInternalFrame frame : frames) {
+      if (frame instanceof SplitFrame && frame != source) {
         SplitFrame sf = (SplitFrame) frame;
         AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
         AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
         boolean gatherThis = false;
-        for (int a = 0; a < topFrame.alignPanels.size(); a++)
-        {
+        for (int a = 0; a < topFrame.alignPanels.size(); a++) {
           AlignmentPanel topPanel = topFrame.alignPanels.get(a);
           AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
           if (topViewId.equals(topPanel.av.getSequenceSetId())
-                  && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
-          {
+              && bottomViewId.equals(bottomPanel.av.getSequenceSetId())) {
             gatherThis = true;
             topPanel.av.setGatherViewsHere(false);
             bottomPanel.av.setGatherViewsHere(false);
-            topPanel.av.setExplodedGeometry(
-                    new Rectangle(sf.getLocation(), topFrame.getSize()));
-            bottomPanel.av.setExplodedGeometry(
-                    new Rectangle(sf.getLocation(), bottomFrame.getSize()));
+            topPanel.av.setExplodedGeometry(new Rectangle(sf.getLocation(), topFrame.getSize()));
+            bottomPanel.av.setExplodedGeometry(new Rectangle(sf.getLocation(), bottomFrame.getSize()));
             myTopFrame.addAlignmentPanel(topPanel, false);
             myBottomFrame.addAlignmentPanel(bottomPanel, false);
           }
         }
 
-        if (gatherThis)
-        {
+        if (gatherThis) {
           topFrame.getAlignPanels().clear();
           bottomFrame.getAlignPanels().clear();
           sf.close();
@@ -3132,8 +2584,7 @@ public class Desktop extends jalview.jbgui.GDesktop
     myTopFrame.setDisplayedView(myTopFrame.alignPanel);
   }
 
-  public static groovy.ui.Console getGroovyConsole()
-  {
+  public static groovy.ui.Console getGroovyConsole() {
     return groovyConsole;
   }
 
@@ -3142,147 +2593,88 @@ public class Desktop extends jalview.jbgui.GDesktop
    * 
    * TODO refactor to desktop utilities class
    * 
-   * @param files
-   *          - Data source strings extracted from the drop event
-   * @param protocols
-   *          - protocol for each data source extracted from the drop event
-   * @param evt
-   *          - the drop event
-   * @param t
-   *          - the payload from the drop event
+   * @param files     - Data source strings extracted from the drop event
+   * @param protocols - protocol for each data source extracted from the drop
+   *                  event
+   * @param evt       - the drop event
+   * @param t         - the payload from the drop event
    * @throws Exception
    */
-  public static void transferFromDropTarget(List<Object> files,
-          List<DataSourceType> protocols, DropTargetDropEvent evt,
-          Transferable t) throws Exception
-  {
-
-    // BH 2018 changed List<String> to List<Object> to allow for File from
-    // SwingJS
-
-    // DataFlavor[] flavors = t.getTransferDataFlavors();
-    // for (int i = 0; i < flavors.length; i++) {
-    // if (flavors[i].isFlavorJavaFileListType()) {
-    // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
-    // List<File> list = (List<File>) t.getTransferData(flavors[i]);
-    // for (int j = 0; j < list.size(); j++) {
-    // File file = (File) list.get(j);
-    // byte[] data = getDroppedFileBytes(file);
-    // fileName.setText(file.getName() + " - " + data.length + " " +
-    // evt.getLocation());
-    // JTextArea target = (JTextArea) ((DropTarget)
-    // evt.getSource()).getComponent();
-    // target.setText(new String(data));
-    // }
-    // dtde.dropComplete(true);
-    // return;
-    // }
-    //
-
-    DataFlavor uriListFlavor = new DataFlavor(
-            "text/uri-list;class=java.lang.String"), urlFlavour = null;
-    try
-    {
-      urlFlavour = new DataFlavor(
-              "application/x-java-url; class=java.net.URL");
-    } catch (ClassNotFoundException cfe)
-    {
+  public static void transferFromDropTarget(List<Object> files, List<DataSourceType> protocols, DropTargetDropEvent evt,
+      Transferable t) throws Exception {
+
+    DataFlavor uriListFlavor = new DataFlavor("text/uri-list;class=java.lang.String"), urlFlavour = null;
+    try {
+      urlFlavour = new DataFlavor("application/x-java-url; class=java.net.URL");
+    } catch (ClassNotFoundException cfe) {
       Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
     }
 
-    if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
-    {
+    if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour)) {
 
-      try
-      {
+      try {
         java.net.URL url = (URL) t.getTransferData(urlFlavour);
         // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
         // means url may be null.
-        if (url != null)
-        {
+        if (url != null) {
           protocols.add(DataSourceType.URL);
           files.add(url.toString());
-          Cache.log.debug("Drop handled as URL dataflavor "
-                  + files.get(files.size() - 1));
+          Cache.log.debug("Drop handled as URL dataflavor " + files.get(files.size() - 1));
           return;
-        }
-        else
-        {
-          if (Platform.isAMacAndNotJS())
-          {
-            System.err.println(
-                    "Please ignore plist error - occurs due to problem with java 8 on OSX");
+        } else {
+          if (Platform.isAMacAndNotJS()) {
+            System.err.println("Please ignore plist error - occurs due to problem with java 8 on OSX");
           }
         }
-      } catch (Throwable ex)
-      {
+      } catch (Throwable ex) {
         Cache.log.debug("URL drop handler failed.", ex);
       }
     }
-    if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
-    {
+    if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
       // Works on Windows and MacOSX
       Cache.log.debug("Drop handled as javaFileListFlavor");
-      for (Object file : (List) t
-              .getTransferData(DataFlavor.javaFileListFlavor))
-      {
+      for (Object file : (List) t.getTransferData(DataFlavor.javaFileListFlavor)) {
         files.add(file);
         protocols.add(DataSourceType.FILE);
       }
-    }
-    else
-    {
+    } else {
       // Unix like behaviour
       boolean added = false;
       String data = null;
-      if (t.isDataFlavorSupported(uriListFlavor))
-      {
+      if (t.isDataFlavorSupported(uriListFlavor)) {
         Cache.log.debug("Drop handled as uriListFlavor");
         // This is used by Unix drag system
         data = (String) t.getTransferData(uriListFlavor);
       }
-      if (data == null)
-      {
+      if (data == null) {
         // fallback to text: workaround - on OSX where there's a JVM bug
         Cache.log.debug("standard URIListFlavor failed. Trying text");
         // try text fallback
-        DataFlavor textDf = new DataFlavor(
-                "text/plain;class=java.lang.String");
-        if (t.isDataFlavorSupported(textDf))
-        {
+        DataFlavor textDf = new DataFlavor("text/plain;class=java.lang.String");
+        if (t.isDataFlavorSupported(textDf)) {
           data = (String) t.getTransferData(textDf);
         }
 
-        Cache.log.debug("Plain text drop content returned "
-                + (data == null ? "Null - failed" : data));
+        Cache.log.debug("Plain text drop content returned " + (data == null ? "Null - failed" : data));
 
       }
-      if (data != null)
-      {
-        while (protocols.size() < files.size())
-        {
-          Cache.log.debug("Adding missing FILE protocol for "
-                  + files.get(protocols.size()));
+      if (data != null) {
+        while (protocols.size() < files.size()) {
+          Cache.log.debug("Adding missing FILE protocol for " + files.get(protocols.size()));
           protocols.add(DataSourceType.FILE);
         }
-        for (java.util.StringTokenizer st = new java.util.StringTokenizer(
-                data, "\r\n"); st.hasMoreTokens();)
-        {
+        for (java.util.StringTokenizer st = new java.util.StringTokenizer(data, "\r\n"); st.hasMoreTokens();) {
           added = true;
           String s = st.nextToken();
-          if (s.startsWith("#"))
-          {
+          if (s.startsWith("#")) {
             // the line is a comment (as per the RFC 2483)
             continue;
           }
           java.net.URI uri = new java.net.URI(s);
-          if (uri.getScheme().toLowerCase().startsWith("http"))
-          {
+          if (uri.getScheme().toLowerCase(Locale.ROOT).startsWith("http")) {
             protocols.add(DataSourceType.URL);
             files.add(uri.toString());
-          }
-          else
-          {
+          } else {
             // otherwise preserve old behaviour: catch all for file objects
             java.io.File file = new java.io.File(uri);
             protocols.add(DataSourceType.FILE);
@@ -3291,69 +2683,46 @@ public class Desktop extends jalview.jbgui.GDesktop
         }
       }
 
-      if (Cache.log.isDebugEnabled())
-      {
-        if (data == null || !added)
-        {
+      if (Cache.log.isDebugEnabled()) {
+        if (data == null || !added) {
 
-          if (t.getTransferDataFlavors() != null
-                  && t.getTransferDataFlavors().length > 0)
-          {
-            Cache.log.debug(
-                    "Couldn't resolve drop data. Here are the supported flavors:");
-            for (DataFlavor fl : t.getTransferDataFlavors())
-            {
-              Cache.log.debug(
-                      "Supported transfer dataflavor: " + fl.toString());
+          if (t.getTransferDataFlavors() != null && t.getTransferDataFlavors().length > 0) {
+            Cache.log.debug("Couldn't resolve drop data. Here are the supported flavors:");
+            for (DataFlavor fl : t.getTransferDataFlavors()) {
+              Cache.log.debug("Supported transfer dataflavor: " + fl.toString());
               Object df = t.getTransferData(fl);
-              if (df != null)
-              {
+              if (df != null) {
                 Cache.log.debug("Retrieves: " + df);
-              }
-              else
-              {
+              } else {
                 Cache.log.debug("Retrieved nothing");
               }
             }
-          }
-          else
-          {
-            Cache.log.debug("Couldn't resolve dataflavor for drop: "
-                    + t.toString());
+          } else {
+            Cache.log.debug("Couldn't resolve dataflavor for drop: " + t.toString());
           }
         }
       }
     }
-    if (Platform.isWindowsAndNotJS())
-    {
+    if (Platform.isWindowsAndNotJS()) {
       Cache.log.debug("Scanning dropped content for Windows Link Files");
 
       // resolve any .lnk files in the file drop
-      for (int f = 0; f < files.size(); f++)
-      {
-        String source = files.get(f).toString().toLowerCase();
+      for (int f = 0; f < files.size(); f++) {
+        String source = files.get(f).toString().toLowerCase(Locale.ROOT);
         if (protocols.get(f).equals(DataSourceType.FILE)
-                && (source.endsWith(".lnk") || source.endsWith(".url")
-                        || source.endsWith(".site")))
-        {
-          try
-          {
+            && (source.endsWith(".lnk") || source.endsWith(".url") || source.endsWith(".site"))) {
+          try {
             Object obj = files.get(f);
-            File lf = (obj instanceof File ? (File) obj
-                    : new File((String) obj));
+            File lf = (obj instanceof File ? (File) obj : new File((String) obj));
             // process link file to get a URL
             Cache.log.debug("Found potential link file: " + lf);
             WindowsShortcut wscfile = new WindowsShortcut(lf);
             String fullname = wscfile.getRealFilename();
             protocols.set(f, FormatAdapter.checkProtocol(fullname));
             files.set(f, fullname);
-            Cache.log.debug("Parsed real filename " + fullname
-                    + " to extract protocol: " + protocols.get(f));
-          } catch (Exception ex)
-          {
-            Cache.log.error(
-                    "Couldn't parse " + files.get(f) + " as a link file.",
-                    ex);
+            Cache.log.debug("Parsed real filename " + fullname + " to extract protocol: " + protocols.get(f));
+          } catch (Exception ex) {
+            Cache.log.error("Couldn't parse " + files.get(f) + " as a link file.", ex);
           }
         }
       }
@@ -3365,40 +2734,30 @@ public class Desktop extends jalview.jbgui.GDesktop
    * depending on the state of the controlling menu item
    */
   @Override
-  protected void showExperimental_actionPerformed(boolean selected)
-  {
+  protected void showExperimental_actionPerformed(boolean selected) {
     Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
   }
 
   /**
-   * Answers a (possibly empty) list of any structure viewer frames (currently
-   * for either Jmol or Chimera) which are currently open. This may optionally
-   * be restricted to viewers of a specified class, or viewers linked to a
-   * specified alignment panel.
+   * Answers a (possibly empty) list of any structure viewer frames (currently for
+   * either Jmol or Chimera) which are currently open. This may optionally be
+   * restricted to viewers of a specified class, or viewers linked to a specified
+   * alignment panel.
    * 
-   * @param apanel
-   *          if not null, only return viewers linked to this panel
-   * @param structureViewerClass
-   *          if not null, only return viewers of this class
+   * @param apanel               if not null, only return viewers linked to this
+   *                             panel
+   * @param structureViewerClass if not null, only return viewers of this class
    * @return
    */
-  public List<StructureViewerBase> getStructureViewers(
-          AlignmentPanel apanel,
-          Class<? extends StructureViewerBase> structureViewerClass)
-  {
+  public List<StructureViewerBase> getStructureViewers(AlignmentPanel apanel,
+      Class<? extends StructureViewerBase> structureViewerClass) {
     List<StructureViewerBase> result = new ArrayList<>();
     JInternalFrame[] frames = Desktop.instance.getAllFrames();
 
-    for (JInternalFrame frame : frames)
-    {
-      if (frame instanceof StructureViewerBase)
-      {
-        if (structureViewerClass == null
-                || structureViewerClass.isInstance(frame))
-        {
-          if (apanel == null
-                  || ((StructureViewerBase) frame).isLinkedWith(apanel))
-          {
+    for (JInternalFrame frame : frames) {
+      if (frame instanceof StructureViewerBase) {
+        if (structureViewerClass == null || structureViewerClass.isInstance(frame)) {
+          if (apanel == null || ((StructureViewerBase) frame).isLinkedWith(apanel)) {
             result.add((StructureViewerBase) frame);
           }
         }
@@ -3411,31 +2770,24 @@ public class Desktop extends jalview.jbgui.GDesktop
 
   private static boolean debugScaleMessageDone = false;
 
-  public static void debugScaleMessage(Graphics g)
-  {
-    if (debugScaleMessageDone)
-    {
+  public static void debugScaleMessage(Graphics g) {
+    if (debugScaleMessageDone) {
       return;
     }
     // output used by tests to check HiDPI scaling settings in action
-    try
-    {
+    try {
       Graphics2D gg = (Graphics2D) g;
-      if (gg != null)
-      {
+      if (gg != null) {
         AffineTransform t = gg.getTransform();
         double scaleX = t.getScaleX();
         double scaleY = t.getScaleY();
         Cache.debug(debugScaleMessage + scaleX + " (X)");
         Cache.debug(debugScaleMessage + scaleY + " (Y)");
         debugScaleMessageDone = true;
-      }
-      else
-      {
+      } else {
         Cache.debug("Desktop graphics null");
       }
-    } catch (Exception e)
-    {
+    } catch (Exception e) {
       Cache.debug(Cache.getStackTraceString(e));
     }
   }
index e636455..572ada6 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Component;
@@ -1529,7 +1531,7 @@ public class FeatureSettings extends JPanel
     String text = MessageManager.formatMessage("label.show_linked_features",
             nucleotide
                     ? MessageManager.getString("label.protein")
-                            .toLowerCase()
+                            .toLowerCase(Locale.ROOT)
                     : "CDS");
     showComplement = new JCheckBox(text);
     showComplement.addActionListener(new ActionListener()
index d328452..93dcadf 100755 (executable)
  */
 package jalview.gui;
 
-import jalview.api.AlignViewportI;
-import jalview.api.FinderI;
-import jalview.datamodel.SearchResultMatchI;
-import jalview.datamodel.SearchResultsI;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.jbgui.GFinder;
-import jalview.util.MessageManager;
+import java.util.Locale;
 
 import java.awt.Dimension;
-import java.awt.Graphics;
 import java.awt.event.ActionEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
 import java.awt.event.KeyEvent;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -48,6 +42,15 @@ import javax.swing.KeyStroke;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
 
+import jalview.api.AlignViewportI;
+import jalview.api.FinderI;
+import jalview.datamodel.SearchResultMatchI;
+import jalview.datamodel.SearchResultsI;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.jbgui.GFinder;
+import jalview.util.MessageManager;
+
 /**
  * Performs the menu option for searching the alignment, for the next or all
  * matches. If matches are found, they are highlighted, and the user has the
@@ -82,33 +85,62 @@ public class Finder extends GFinder
 
   private SearchResultsI searchResults;
 
+  /*
+   * true if Finder always acts on the same alignment,
+   * false if it acts on the alignment with focus
+   */
+  private boolean focusFixed;
+
   /**
    * Constructor given an associated alignment panel. Constructs and displays an
-   * internal frame where the user can enter a search string.
+   * internal frame where the user can enter a search string. The Finder may
+   * have 'fixed focus' (always act the panel for which it is constructed), or
+   * not (acts on the alignment that has focus). An optional 'scope' may be
+   * added to be shown in the title of the Finder frame.
    * 
    * @param alignPanel
+   * @param fixedFocus
+   * @param scope
    */
-  public Finder(AlignmentPanel alignPanel)
+  public Finder(AlignmentPanel alignPanel, boolean fixedFocus, String scope)
   {
     av = alignPanel.getAlignViewport();
     ap = alignPanel;
+    focusFixed = fixedFocus;
     finders = new HashMap<>();
     frame = new JInternalFrame();
     frame.setContentPane(this);
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
-    frame.addInternalFrameListener(
-            new InternalFrameAdapter()
-            {
-              @Override
-              public void internalFrameClosing(InternalFrameEvent e)
-              {
-                closeAction();
-              }
-            });
+    frame.addInternalFrameListener(new InternalFrameAdapter()
+    {
+      @Override
+      public void internalFrameClosing(InternalFrameEvent e)
+      {
+        closeAction();
+      }
+    });
+    frame.addFocusListener(new FocusAdapter()
+    {
+      @Override
+      public void focusGained(FocusEvent e)
+      {
+        /*
+         * ensure 'ignore hidden columns' is only enabled
+         * if the alignment with focus has hidden columns
+         */
+        getFocusedViewport();
+      }
+    });
+
     addEscapeHandler();
 
-    Desktop.addInternalFrame(frame, MessageManager.getString("label.find"),
-            true, MY_WIDTH, MY_HEIGHT, true, true);
+    String title = MessageManager.getString("label.find");
+    if (scope != null)
+    {
+      title += " " + scope;
+    }
+    Desktop.addInternalFrame(frame, title, MY_WIDTH, MY_HEIGHT);
+    frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
     searchBox.getComponent().requestFocus();
   }
 
@@ -156,17 +188,18 @@ public class Finder extends GFinder
   /**
    * if !focusfixed and not in a desktop environment, checks that av and ap are
    * valid. Otherwise, gets the topmost alignment window and sets av and ap
-   * accordingly. Also sets the 'ignore hidden' checkbox disabled if the viewport
-   * has no hidden columns.
+   * accordingly. Also sets the 'ignore hidden' checkbox disabled if the
+   * viewport has no hidden columns.
    * 
    * @return false if no alignment window was found
    */
   boolean getFocusedViewport()
   {
-    if (Desktop.desktop == null)
+    if (focusFixed || Desktop.desktop == null)
     {
       if (ap != null && av != null)
       {
+        ignoreHidden.setEnabled(av.hasHiddenColumns());
         return true;
       }
       // we aren't in a desktop environment, so give up now.
@@ -294,19 +327,25 @@ public class Finder extends GFinder
       if (doFindAll)
       {
         // then we report the matches that were found
-        String message = (idMatch.size() > 0) ? "" + idMatch.size() + " IDs"
-                : "";
+        StringBuilder message = new StringBuilder();
+        if (idMatch.size() > 0)
+        {
+          message.append(idMatch.size()).append(" IDs");
+        }
         if (searchResults != null)
         {
           if (idMatch.size() > 0 && searchResults.getCount() > 0)
           {
-            message += " and ";
+            message.append(" ").append(
+                    MessageManager.getString("label.and").toLowerCase(Locale.ROOT))
+                    .append(" ");
           }
-          message += searchResults.getCount()
-                  + " subsequence matches found.";
+          message.append(MessageManager.formatMessage(
+                  "label.subsequence_matches_found",
+                  searchResults.getCount()));
         }
-        JvOptionPane.showInternalMessageDialog(this, message, null,
-                JvOptionPane.PLAIN_MESSAGE);
+        JvOptionPane.showInternalMessageDialog(this, message.toString(),
+                null, JvOptionPane.INFORMATION_MESSAGE);
       }
     }
   }
@@ -371,15 +410,4 @@ public class Finder extends GFinder
       ap.alignFrame.requestFocus();
     }
   }
-
-  @Override
-  protected void paintComponent(Graphics g)
-  {
-    /*
-     * enable 'hidden regions' option only if
-     * 'top' viewport has hidden columns
-     */
-    getFocusedViewport();
-    super.paintComponent(g);
-  }
 }
index 8d62433..7bdcb2e 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import jalview.util.MessageManager;
 import jalview.ws.seqfetcher.DbSourceProxy;
 
@@ -227,11 +229,11 @@ public class JDatabaseTree extends JalviewDialog implements KeyListener
         if (child.getUserObject() instanceof DbSourceProxy)
         {
           names[i] = ((DbSourceProxy) child.getUserObject()).getDbName()
-                  .toLowerCase();
+                  .toLowerCase(Locale.ROOT);
         }
         else
         {
-          names[i] = ((String) child.getUserObject()).toLowerCase();
+          names[i] = ((String) child.getUserObject()).toLowerCase(Locale.ROOT);
           sortTreeNodes(child);
         }
       }
index 5b7a928..cfd44c7 100644 (file)
@@ -42,7 +42,7 @@ public class JalviewChimeraXBindingModel extends JalviewChimeraBindingModel
     int modelNumber = chimeraMaps.size() + 1;
     String command = "setattr #" + modelNumber + " models name "
             + pe.getId();
-    executeCommand(new StructureCommand(command), false);
+   executeCommand(new StructureCommand(command), false);
     modelsToMap.add(new ChimeraModel(pe.getId(), ModelType.PDB_MODEL,
             modelNumber, 0));
   }
@@ -93,6 +93,7 @@ public class JalviewChimeraXBindingModel extends JalviewChimeraBindingModel
    * @param atomSpec
    * @return
    */
+  @Override
   protected AtomSpec parseAtomSpec(String atomSpec)
   {
     return AtomSpec.fromChimeraXAtomspec(atomSpec);
index 0f4d0e7..53b0305 100644 (file)
  */
 package jalview.gui;
 
-import jalview.util.MessageManager;
-import jalview.ws.params.ArgumentI;
-import jalview.ws.params.OptionI;
-import jalview.ws.params.ParameterI;
-import jalview.ws.params.ValueConstrainI;
-import jalview.ws.params.ValueConstrainI.ValueType;
-
 import java.awt.BorderLayout;
 import java.awt.Component;
 import java.awt.Dimension;
@@ -35,6 +28,8 @@ import java.awt.GridLayout;
 import java.awt.Rectangle;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
 import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
 import java.awt.event.MouseEvent;
@@ -59,6 +54,12 @@ import javax.swing.border.TitledBorder;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 
+import jalview.util.MessageManager;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.OptionI;
+import jalview.ws.params.ParameterI;
+import jalview.ws.params.ValueConstrainI;
+import jalview.ws.params.ValueConstrainI.ValueType;
 import net.miginfocom.swing.MigLayout;
 
 /**
@@ -92,7 +93,7 @@ public class OptsAndParamsPage
 
     JLabel optlabel = new JLabel();
 
-    JComboBox val = new JComboBox();
+    JComboBox<String> val = new JComboBox<>();
 
     public OptionBox(OptionI opt)
     {
@@ -126,7 +127,7 @@ public class OptsAndParamsPage
         }
       }
       add(enabled, BorderLayout.NORTH);
-      for (Object str : opt.getPossibleValues())
+      for (String str : opt.getPossibleValues())
       {
         val.addItem(str);
       }
@@ -588,7 +589,7 @@ public class OptsAndParamsPage
       {
         if (choice)
         {
-          choicebox = new JComboBox();
+          choicebox = new JComboBox<>();
           choicebox.addActionListener(this);
           controlPanel.add(choicebox, BorderLayout.CENTER);
         }
@@ -621,8 +622,19 @@ public class OptsAndParamsPage
             {
             }
           });
+          valueField.addFocusListener(new FocusAdapter() {
+
+            @Override
+            public void focusLost(FocusEvent e)
+            {
+              actionPerformed(null);
+            }
+            
+          });
           valueField.setPreferredSize(new Dimension(60, 25));
+          valueField.setText(parm.getValue());
           slider = makeSlider(parm.getValidValue());
+          updateSliderFromValueField();
           slider.addChangeListener(this);
 
           controlPanel.add(slider, BorderLayout.WEST);
index 2a7fb9f..6b32efa 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.event.ActionEvent;
@@ -1125,7 +1127,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
         for (int d = 0; d < nd; d++)
         {
           DBRefEntry e = dbr.get(d);
-          String src = e.getSource(); // jalview.util.DBRefUtils.getCanonicalName(dbr[d].getSource()).toUpperCase();
+          String src = e.getSource(); // jalview.util.DBRefUtils.getCanonicalName(dbr[d].getSource()).toUpperCase(Locale.ROOT);
           Object[] sarray = commonDbrefs.get(src);
           if (sarray == null)
           {
@@ -1171,7 +1173,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener
       boolean usingNames = false;
       // Now see which parts of the group apply for this URL
       String ltarget = urlLink.getTarget(); // jalview.util.DBRefUtils.getCanonicalName(urlLink.getTarget());
-      Object[] idset = commonDbrefs.get(ltarget.toUpperCase());
+      Object[] idset = commonDbrefs.get(ltarget.toUpperCase(Locale.ROOT));
       String[] seqstr, ids; // input to makeUrl
       if (idset != null)
       {
index 6972657..baed0df 100755 (executable)
@@ -31,6 +31,7 @@ import java.awt.event.MouseEvent;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
 
 import javax.help.HelpSetException;
 import javax.swing.JComboBox;
@@ -145,10 +146,10 @@ public class Preferences extends GPreferences
 
   /**
    * Holds name and link separated with | character. Sequence IDS and Sequences
-   * must be $SEQUENCEIDS$ or $SEQUENCEIDS=/.possible | chars ./=$ and
-   * $SEQUENCES$ or $SEQUENCES=/.possible | chars ./=$ and separation character
-   * for first and second token specified after a pipe character at end |,|.
-   * (TODO: proper escape for using | to separate ids or sequences
+   * must be $SEQUENCEIDS$ or $SEQUENCEIDS=/.possible | chars ./=$ and $SEQUENCES$
+   * or $SEQUENCES=/.possible | chars ./=$ and separation character for first and
+   * second token specified after a pipe character at end |,|. (TODO: proper
+   * escape for using | to separate ids or sequences
    */
 
   public static List<String> groupURLLinks;
@@ -219,19 +220,27 @@ public class Preferences extends GPreferences
 
   public static void openPreferences()
   {
-    openPreferences(0, null);
+    openPreferences(null, null);
   }
 
-  public static void openPreferences(int selectTab, String message)
+  public static void openPreferences(TabRef selectTab, String message)
   {
     Preferences p = getInstance();
-    p.selectTab(selectTab);
-    p.setMessage(message);
+    if (selectTab != null)
+      p.selectTab(selectTab, message);
     p.frame.show();
     p.frame.moveToFront();
     p.frame.grabFocus();
   }
 
+  public void selectTab(TabRef selectTab, String message)
+  {
+    this.selectTab(selectTab);
+    if (message != null)
+      this.setMessage(message);
+    this.frame.show();
+  }
+
   /**
    * Creates a new Preferences object.
    */
@@ -346,8 +355,8 @@ public class Preferences extends GPreferences
     startupCheckbox
             .setSelected(Cache.getDefault("SHOW_STARTUP_FILE", true));
     startupFileTextfield.setText(Cache.getDefault("STARTUP_FILE",
-            Cache.getDefault("www.jalview.org", "http://www.jalview.org")
-                    + "/examples/exampleFile_2_3.jar"));
+            Cache.getDefault("www.jalview.org", "https://www.jalview.org")
+                    + "/examples/exampleFile_2_7.jvp"));
 
     /*
      * Set Colours tab defaults
@@ -437,11 +446,27 @@ public class Preferences extends GPreferences
       {
         if (validateViewerPath())
         {
-          Cache.setProperty(
-                  structViewer.getSelectedItem().equals(
-                          ViewerType.CHIMERAX.name()) ? CHIMERAX_PATH
-                                  : CHIMERA_PATH,
-                  structureViewerPath.getText());
+          String path = structureViewerPath.getText();
+          try {
+          ViewerType type = ViewerType.valueOf(viewerType);
+          switch (type)
+          {
+          case JMOL:
+            break;
+          case CHIMERA:
+            Cache.setProperty(CHIMERA_PATH, path);
+            break;
+          case CHIMERAX:
+            Cache.setProperty(CHIMERAX_PATH, path);
+            break;
+          case PYMOL:
+            Cache.setProperty(PYMOL_PATH, path);
+            break;
+          }
+        } catch (IllegalArgumentException x)
+        {
+          Cache.log.error("Failed to set path - unknown viewer type",x);
+        }
         }
       }
     });
@@ -1009,8 +1034,8 @@ public class Preferences extends GPreferences
   }
 
   /**
-   * Do any necessary validation before saving settings. Return focus to the
-   * first tab which fails validation.
+   * Do any necessary validation before saving settings. Return focus to the first
+   * tab which fails validation.
    * 
    * @return
    */
@@ -1064,7 +1089,7 @@ public class Preferences extends GPreferences
    * DOCUMENT ME!
    * 
    * @param e
-   *          DOCUMENT ME!
+   *            DOCUMENT ME!
    */
   @Override
   public void cancel_actionPerformed(ActionEvent e)
@@ -1086,7 +1111,7 @@ public class Preferences extends GPreferences
    * DOCUMENT ME!
    * 
    * @param e
-   *          DOCUMENT ME!
+   *            DOCUMENT ME!
    */
   @Override
   public void annotations_actionPerformed(ActionEvent e)
@@ -1349,8 +1374,8 @@ public class Preferences extends GPreferences
   }
 
   /**
-   * Returns true if structure viewer path is to a valid executable, else shows
-   * an error dialog. Does nothing if the path is empty, as is the case for Jmol
+   * Returns true if structure viewer path is to a valid executable, else shows an
+   * error dialog. Does nothing if the path is empty, as is the case for Jmol
    * (built in to Jalview) or when Jalview is left to try default paths.
    */
   private boolean validateViewerPath()
@@ -1371,8 +1396,8 @@ public class Preferences extends GPreferences
   }
 
   /**
-   * If Chimera or ChimeraX or Pymol is selected, check it can be found on
-   * default or user-specified path, if not show a warning/help dialog
+   * If Chimera or ChimeraX or Pymol is selected, check it can be found on default
+   * or user-specified path, if not show a warning/help dialog
    */
   @Override
   protected void structureViewer_actionPerformed(String selectedItem)
@@ -1439,8 +1464,10 @@ public class Preferences extends GPreferences
                       MessageManager.getString("label.viewer_missing")),
               "", JvOptionPane.YES_NO_OPTION, JvOptionPane.WARNING_MESSAGE,
               null, options, options[0]);
+
       if (showHelp == JvOptionPane.NO_OPTION)
       {
+        this.selectTab(Preferences.TabRef.STRUCTURE_TAB, null);
         try
         {
           Help.showHelpWindow(HelpId.StructureViewer);
@@ -1449,6 +1476,24 @@ public class Preferences extends GPreferences
           e.printStackTrace();
         }
       }
+      else if (showHelp == JvOptionPane.OK_OPTION)
+      {
+        this.selectTab(Preferences.TabRef.STRUCTURE_TAB, null);
+        CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> {
+          try
+          {
+            for (int i = 0; i < 3; i++)
+            {
+              structureViewerPath.setBackground(Color.PINK);
+              Thread.sleep(500);
+              structureViewerPath.setBackground(Color.WHITE);
+              Thread.sleep(500);
+            }
+          } catch (InterruptedException e)
+          {
+          }
+        });
+      }
     }
   }
 
index b27208a..a9b9d4d 100755 (executable)
@@ -456,7 +456,7 @@ public class SeqCanvas extends JPanel implements ViewportListenerI
 
       if (av.getWrapAlignment())
       {
-        drawWrappedPanel(gg, width, height, ranges.getStartRes());
+        drawWrappedPanel(gg, getWidth(), getHeight(), ranges.getStartRes());
       }
       else
       {
index 4ffefba..454a730 100644 (file)
@@ -275,6 +275,9 @@ public class SeqPanel extends JPanel
   /**
    * Computes the column and sequence row (and possibly annotation row when in
    * wrapped mode) for the given mouse position
+   * <p>
+   * Mouse position is not set if in wrapped mode with the cursor either between
+   * sequences, or over the left or right vertical scale.
    * 
    * @param evt
    * @return
@@ -342,6 +345,9 @@ public class SeqPanel extends JPanel
   /**
    * Returns the aligned sequence position (base 0) at the mouse position, or
    * the closest visible one
+   * <p>
+   * Returns -1 if in wrapped mode with the mouse over either left or right
+   * vertical scale.
    * 
    * @param evt
    * @return
@@ -477,47 +483,80 @@ public class SeqPanel extends JPanel
 
   void moveCursor(int dx, int dy)
   {
-    seqCanvas.cursorX += dx;
-    seqCanvas.cursorY += dy;
-
+    moveCursor(dx, dy,false);
+  }
+  void moveCursor(int dx, int dy, boolean nextWord)
+  {
     HiddenColumns hidden = av.getAlignment().getHiddenColumns();
 
-    if (av.hasHiddenColumns() && !hidden.isVisible(seqCanvas.cursorX))
+    if (nextWord)
     {
-      int original = seqCanvas.cursorX - dx;
       int maxWidth = av.getAlignment().getWidth();
-
-      if (!hidden.isVisible(seqCanvas.cursorX))
-      {
-        int visx = hidden.absoluteToVisibleColumn(seqCanvas.cursorX - dx);
-        int[] region = hidden.getRegionWithEdgeAtRes(visx);
-
-        if (region != null) // just in case
+      int maxHeight=av.getAlignment().getHeight();
+      SequenceI seqAtRow = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
+      // look for next gap or residue
+      boolean isGap = Comparison.isGap(seqAtRow.getCharAt(seqCanvas.cursorX));
+      int p = seqCanvas.cursorX,lastP,r=seqCanvas.cursorY,lastR;
+      do
+      {
+        lastP = p;
+        lastR = r;
+        if (dy != 0)
         {
-          if (dx == 1)
+          r += dy;
+          if (r < 0)
           {
-            // moving right
-            seqCanvas.cursorX = region[1] + 1;
+            r = 0;
           }
-          else if (dx == -1)
+          if (r >= maxHeight)
           {
-            // moving left
-            seqCanvas.cursorX = region[0] - 1;
+            r = maxHeight - 1;
           }
+          seqAtRow = av.getAlignment().getSequenceAt(r);
         }
-        seqCanvas.cursorX = (seqCanvas.cursorX < 0) ? 0 : seqCanvas.cursorX;
-      }
+        p = nextVisible(hidden, maxWidth, p, dx);
+      } while ((dx != 0 ? p != lastP : r != lastR)
+              && isGap == Comparison.isGap(seqAtRow.getCharAt(p)));
+      seqCanvas.cursorX=p;
+      seqCanvas.cursorY=r;
+    } else {
+      int maxWidth = av.getAlignment().getWidth();
+      seqCanvas.cursorX = nextVisible(hidden, maxWidth, seqCanvas.cursorX, dx);
+      seqCanvas.cursorY += dy;
+    }
+    scrollToVisible(false);
+  }
 
-      if (seqCanvas.cursorX >= maxWidth
-              || !hidden.isVisible(seqCanvas.cursorX))
+  private int nextVisible(HiddenColumns hidden,int maxWidth, int original, int dx)
+  {
+    int newCursorX=original+dx;
+    if (av.hasHiddenColumns() && !hidden.isVisible(newCursorX))
+    {
+      int visx = hidden.absoluteToVisibleColumn(newCursorX - dx);
+      int[] region = hidden.getRegionWithEdgeAtRes(visx);
+
+      if (region != null) // just in case
       {
-        seqCanvas.cursorX = original;
+        if (dx == 1)
+        {
+          // moving right
+          newCursorX = region[1] + 1;
+        }
+        else if (dx == -1)
+        {
+          // moving left
+          newCursorX = region[0] - 1;
+        }
       }
     }
-
-    scrollToVisible(false);
+    newCursorX = (newCursorX < 0) ? 0 : newCursorX;
+    if (newCursorX >= maxWidth
+            || !hidden.isVisible(newCursorX))
+    {
+      newCursorX = original;
+    }
+    return newCursorX;
   }
-
   /**
    * Scroll to make the cursor visible in the viewport.
    * 
index 8b5d3b7..54aefc4 100755 (executable)
  */
 package jalview.gui;
 
-import jalview.api.FeatureSettingsModelI;
-import jalview.bin.Cache;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.SequenceI;
-import jalview.fts.core.GFTSPanel;
-import jalview.fts.service.pdb.PDBFTSPanel;
-import jalview.fts.service.uniprot.UniprotFTSPanel;
-import jalview.io.FileFormatI;
-import jalview.io.gff.SequenceOntologyI;
-import jalview.util.DBRefUtils;
-import jalview.util.MessageManager;
-import jalview.util.Platform;
-import jalview.ws.seqfetcher.DbSourceProxy;
-
 import java.awt.BorderLayout;
 import java.awt.Font;
 import java.awt.event.ActionEvent;
@@ -57,6 +42,22 @@ import javax.swing.JScrollPane;
 import javax.swing.JTextArea;
 import javax.swing.SwingConstants;
 
+import jalview.api.FeatureSettingsModelI;
+import jalview.bin.Cache;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.SequenceI;
+import jalview.fts.core.GFTSPanel;
+import jalview.fts.service.pdb.PDBFTSPanel;
+import jalview.fts.service.threedbeacons.TDBeaconsFTSPanel;
+import jalview.fts.service.uniprot.UniprotFTSPanel;
+import jalview.io.FileFormatI;
+import jalview.io.gff.SequenceOntologyI;
+import jalview.util.DBRefUtils;
+import jalview.util.MessageManager;
+import jalview.util.Platform;
+import jalview.ws.seqfetcher.DbSourceProxy;
+
 /**
  * A panel where the use may choose a database source, and enter one or more
  * accessions, to retrieve entries from the database.
@@ -66,11 +67,50 @@ import javax.swing.SwingConstants;
  */
 public class SequenceFetcher extends JPanel implements Runnable
 {
+  private class StringPair
+  {
+    private String key;
+
+    private String display;
+
+    public StringPair(String s1, String s2)
+    {
+      key = s1;
+      display = s2;
+    }
+
+    public StringPair(String s)
+    {
+      this(s, s);
+    }
+
+    public String getKey()
+    {
+      return key;
+    }
+
+    public String getDisplay()
+    {
+      return display;
+    }
+
+    @Override
+    public String toString()
+    {
+      return display;
+    }
+
+    public boolean equals(StringPair other)
+    {
+      return other.key == this.key;
+    }
+  }
+
   private static jalview.ws.SequenceFetcher sfetch = null;
 
   JLabel exampleAccession;
 
-  JComboBox<String> database;
+  JComboBox<StringPair> database;
 
   JCheckBox replacePunctuation;
 
@@ -145,8 +185,8 @@ public class SequenceFetcher extends JPanel implements Runnable
 
     frame = new JInternalFrame();
     frame.setContentPane(this);
-    Desktop.addInternalFrame(frame, getFrameTitle(), true, 400, 
-               Platform.isAMacAndNotJS() ? 240 : 180);
+    Desktop.addInternalFrame(frame, getFrameTitle(), true, 400,
+            Platform.isAMacAndNotJS() ? 240 : 180);
   }
 
   private String getFrameTitle()
@@ -163,15 +203,25 @@ public class SequenceFetcher extends JPanel implements Runnable
 
     database = new JComboBox<>();
     database.setFont(JvSwingUtils.getLabelFont());
-    database.setPrototypeDisplayValue("ENSEMBLGENOMES   ");
+    StringPair instructionItem = new StringPair(
+            MessageManager.getString("action.select_ddbb"));
+    database.setPrototypeDisplayValue(instructionItem);
     String[] sources = new jalview.ws.SequenceFetcher().getSupportedDb();
     Arrays.sort(sources, String.CASE_INSENSITIVE_ORDER);
-    database.addItem(MessageManager.getString("action.select_ddbb"));
+    database.addItem(instructionItem);
     for (String source : sources)
     {
-      database.addItem(source);
+      List<DbSourceProxy> slist = sfetch.getSourceProxy(source);
+      if (slist.size() == 1 && slist.get(0) != null)
+      {
+        database.addItem(new StringPair(source, slist.get(0).getDbName()));
+      }
+      else
+      {
+        database.addItem(new StringPair(source));
+      }
     }
-    database.setSelectedItem(selectedDb);
+    setDatabaseSelectedItem(selectedDb);
     if (database.getSelectedIndex() == -1)
     {
       database.setSelectedIndex(0);
@@ -182,7 +232,8 @@ public class SequenceFetcher extends JPanel implements Runnable
       @Override
       public void actionPerformed(ActionEvent e)
       {
-        String currentSelection = (String) database.getSelectedItem();
+        String currentSelection = ((StringPair) database.getSelectedItem())
+                .getKey();
         updateExampleQuery(currentSelection);
 
         if ("pdb".equalsIgnoreCase(currentSelection))
@@ -195,6 +246,11 @@ public class SequenceFetcher extends JPanel implements Runnable
           frame.dispose();
           new UniprotFTSPanel(SequenceFetcher.this);
         }
+        else if ("3d-beacons".equalsIgnoreCase(currentSelection))
+        {
+          frame.dispose();
+          new TDBeaconsFTSPanel(SequenceFetcher.this);
+        }
         else
         {
           otherSourceAction();
@@ -304,6 +360,19 @@ public class SequenceFetcher extends JPanel implements Runnable
     this.add(databasePanel, BorderLayout.NORTH);
   }
 
+  private void setDatabaseSelectedItem(String db)
+  {
+    for (int i = 0; i < database.getItemCount(); i++)
+    {
+      StringPair sp = database.getItemAt(i);
+      if (sp != null && db != null && db.equals(sp.getKey()))
+      {
+        database.setSelectedIndex(i);
+        return;
+      }
+    }
+  }
+
   /**
    * Answers a semi-colon-delimited string with the example query or queries for
    * the selected database
@@ -375,7 +444,8 @@ public class SequenceFetcher extends JPanel implements Runnable
    */
   protected void example_actionPerformed()
   {
-    String eq = getExampleQueries((String) database.getSelectedItem());
+    String eq = getExampleQueries(
+            ((StringPair) database.getSelectedItem()).getKey());
     textArea.setText(eq);
     repaint();
   }
@@ -424,9 +494,9 @@ public class SequenceFetcher extends JPanel implements Runnable
       text = text.replace(",", ";");
     }
     text = text.replaceAll("(\\s|[; ])+", ";");
-    if (!t0.equals(text)) 
+    if (!t0.equals(text))
     {
-         textArea.setText(text);
+      textArea.setText(text);
     }
     if (text.isEmpty())
     {
@@ -471,8 +541,8 @@ public class SequenceFetcher extends JPanel implements Runnable
     List<String> presultTitle = new ArrayList<>();
     List<AlignmentI> presult = new ArrayList<>();
     List<AlignmentI> aresult = new ArrayList<>();
-    List<DbSourceProxy> sources = sfetch
-            .getSourceProxy((String) database.getSelectedItem());
+    List<DbSourceProxy> sources = sfetch.getSourceProxy(
+            ((StringPair) database.getSelectedItem()).getKey());
     Iterator<DbSourceProxy> proxies = sources.iterator();
     String[] qries = textArea.getText().trim().split(";");
     List<String> nextFetch = Arrays.asList(qries);
@@ -524,23 +594,25 @@ public class SequenceFetcher extends JPanel implements Runnable
       } catch (Exception e)
       {
         showErrorMessage("Error retrieving " + textArea.getText() + " from "
-                + database.getSelectedItem());
+                + ((StringPair) database.getSelectedItem()).getDisplay());
         // error
         // +="Couldn't retrieve sequences from "+database.getSelectedItem();
         System.err.println("Retrieval failed for source ='"
-                + database.getSelectedItem() + "' and query\n'"
-                + textArea.getText() + "'\n");
+                + ((StringPair) database.getSelectedItem()).getDisplay()
+                + "' and query\n'" + textArea.getText() + "'\n");
         e.printStackTrace();
       } catch (OutOfMemoryError e)
       {
         showErrorMessage("Out of Memory when retrieving "
-                + textArea.getText() + " from " + database.getSelectedItem()
+                + textArea.getText() + " from "
+                + ((StringPair) database.getSelectedItem()).getDisplay()
                 + "\nPlease see the Jalview FAQ for instructions for increasing the memory available to Jalview.\n");
         e.printStackTrace();
       } catch (Error e)
       {
         showErrorMessage("Serious Error retrieving " + textArea.getText()
-                + " from " + database.getSelectedItem());
+                + " from "
+                + ((StringPair) database.getSelectedItem()).getDisplay());
         e.printStackTrace();
       }
 
@@ -687,7 +759,8 @@ public class SequenceFetcher extends JPanel implements Runnable
     } catch (OutOfMemoryError oome)
     {
       new OOMWarning("fetching " + multiacc + " from "
-              + database.getSelectedItem(), oome, this);
+              + ((StringPair) database.getSelectedItem()).getDisplay(),
+              oome, this);
     }
   }
 
@@ -761,12 +834,12 @@ public class SequenceFetcher extends JPanel implements Runnable
 
     for (String q : queries)
     {
-       // BH 2019.01.25 dbr is never used.
-//      DBRefEntry dbr = new DBRefEntry();
-//      dbr.setSource(proxy.getDbSource());
-//      dbr.setVersion(null);
+      // BH 2019.01.25 dbr is never used.
+      // DBRefEntry dbr = new DBRefEntry();
+      // dbr.setSource(proxy.getDbSource());
+      // dbr.setVersion(null);
       String accId = proxy.getAccessionIdFromQuery(q);
-//      dbr.setAccessionId(accId);
+      // dbr.setAccessionId(accId);
       boolean rfound = false;
       for (int r = 0, nr = rs.length; r < nr; r++)
       {
@@ -795,10 +868,20 @@ public class SequenceFetcher extends JPanel implements Runnable
    */
   public String getDefaultRetrievalTitle()
   {
-    return "Retrieved from " + database.getSelectedItem();
+    return "Retrieved from "
+            + ((StringPair) database.getSelectedItem()).getDisplay();
   }
 
-  AlignmentI parseResult(AlignmentI al, String title,
+  /**
+   * constructs an alignment frame given the data and metadata
+   * 
+   * @param al
+   * @param title
+   * @param currentFileFormat
+   * @param preferredFeatureColours
+   * @return the alignment
+   */
+  public AlignmentI parseResult(AlignmentI al, String title,
           FileFormatI currentFileFormat,
           FeatureSettingsModelI preferredFeatureColours)
   {
@@ -831,10 +914,7 @@ public class SequenceFetcher extends JPanel implements Runnable
           }
         }
 
-        if (preferredFeatureColours != null)
-        {
-          af.getViewport().applyFeaturesStyle(preferredFeatureColours);
-        }
+        af.getViewport().applyFeaturesStyle(preferredFeatureColours);
         if (Cache.getDefault("HIDE_INTRONS", true))
         {
           af.hideFeatureColumns(SequenceOntologyI.EXON, false);
index 7f18461..714e770 100644 (file)
@@ -16,6 +16,12 @@ import javax.swing.JSlider;
 public class Slider extends JSlider
 {
   /*
+   * the number of nominal positions the slider represents
+   * (higher number = more fine-grained positioning)
+   */
+  private static final int SCALE_TICKS = 1000;
+
+  /*
    * 'true' value corresponding to zero on the slider
    */
   private float trueMin;
@@ -57,7 +63,7 @@ public class Slider extends JSlider
     trueMin = min;
     trueMax = max;
     setMinimum(0);
-    sliderScaleFactor = 100f / (max - min);
+    sliderScaleFactor = SCALE_TICKS / (max - min);
     int sliderMax = (int) ((max - min) * sliderScaleFactor);
     setMaximum(sliderMax);
     setSliderValue(value);
index 7ade797..7ce8673 100644 (file)
@@ -814,7 +814,11 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI
         if (c != null && c instanceof AlignFrame)
         {
           AlignFrame af = (AlignFrame) c;
-          new Finder(af.alignPanel);
+          boolean dna = af.getViewport().getAlignment().isNucleotide();
+          String scope = MessageManager.getString("label.in") + " "
+                  + (dna ? MessageManager.getString("label.nucleotide")
+                          : MessageManager.getString("label.protein"));
+          new Finder(af.alignPanel, true, scope);
         }
       }
     };
index 33d8c33..09eb7af 100644 (file)
 
 package jalview.gui;
 
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.Executors;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JTable;
+import javax.swing.SwingUtilities;
+import javax.swing.table.AbstractTableModel;
+
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.SequenceI;
 import jalview.fts.api.FTSData;
 import jalview.fts.api.FTSDataColumnI;
 import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSDataColumnPreferences;
 import jalview.fts.core.FTSRestRequest;
 import jalview.fts.core.FTSRestResponse;
 import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.fts.service.threedbeacons.TDB_FTSData;
+import jalview.gui.structurechooser.PDBStructureChooserQuerySource;
+import jalview.gui.structurechooser.StructureChooserQuerySource;
+import jalview.gui.structurechooser.ThreeDBStructureChooserQuerySource;
 import jalview.io.DataSourceType;
+import jalview.jbgui.FilterOption;
 import jalview.jbgui.GStructureChooser;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.MessageManager;
 import jalview.ws.DBRefFetcher;
+import jalview.ws.DBRefFetcher.FetchFinishedListenerI;
+import jalview.ws.seqfetcher.DbSourceProxy;
 import jalview.ws.sifts.SiftsSettings;
 
-import java.awt.event.ItemEvent;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.Vector;
-
-import javax.swing.JCheckBox;
-import javax.swing.JComboBox;
-import javax.swing.JLabel;
-import javax.swing.JTable;
-import javax.swing.SwingUtilities;
-import javax.swing.table.AbstractTableModel;
-
 /**
  * Provides the behaviors for the Structure chooser Panel
  * 
@@ -71,8 +81,6 @@ public class StructureChooser extends GStructureChooser
 {
   private static final String AUTOSUPERIMPOSE = "AUTOSUPERIMPOSE";
 
-  private static int MAX_QLENGTH = 7820;
-
   private SequenceI selectedSequence;
 
   private SequenceI[] selectedSequences;
@@ -81,9 +89,13 @@ public class StructureChooser extends GStructureChooser
 
   private Collection<FTSData> discoveredStructuresSet;
 
-  private FTSRestRequest lastPdbRequest;
+  private StructureChooserQuerySource data;
 
-  private FTSRestClientI pdbRestClient;
+  @Override
+  protected FTSDataColumnPreferences getFTSDocFieldPrefs()
+  {
+    return data.getDocFieldPrefs();
+  }
 
   private String selectedPdbFileName;
 
@@ -91,19 +103,77 @@ public class StructureChooser extends GStructureChooser
 
   private boolean cachedPDBExists;
 
+  private Collection<FTSData> lastDiscoveredStructuresSet;
+
+  private boolean canQueryTDB = false;
+
+  private boolean notQueriedTDBYet = true;
+
+  List<SequenceI> seqsWithoutSourceDBRef = null;
+
   private static StructureViewer lastTargetedView = null;
 
   public StructureChooser(SequenceI[] selectedSeqs, SequenceI selectedSeq,
           AlignmentPanel ap)
   {
+    // which FTS engine to use
+    data = StructureChooserQuerySource.getQuerySourceFor(selectedSeqs);
+    initDialog();
+
     this.ap = ap;
     this.selectedSequence = selectedSeq;
     this.selectedSequences = selectedSeqs;
     this.progressIndicator = (ap == null) ? null : ap.alignFrame;
     init();
+
   }
 
   /**
+   * sets canQueryTDB if protein sequences without a canonical uniprot ref or at
+   * least one structure are discovered.
+   */
+  private void populateSeqsWithoutSourceDBRef()
+  {
+    seqsWithoutSourceDBRef = new ArrayList<SequenceI>();
+    boolean needCanonical = false;
+    for (SequenceI seq : selectedSequences)
+    {
+      if (seq.isProtein())
+      {
+        int dbRef = ThreeDBStructureChooserQuerySource
+                .checkUniprotRefs(seq.getDBRefs());
+        if (dbRef < 0)
+        {
+          if (dbRef == -1)
+          {
+            // need to retrieve canonicals
+            needCanonical = true;
+            seqsWithoutSourceDBRef.add(seq);
+          }
+          else
+          {
+            // could be a sequence with pdb ref
+            if (seq.getAllPDBEntries() == null
+                    || seq.getAllPDBEntries().size() == 0)
+            {
+              seqsWithoutSourceDBRef.add(seq);
+            }
+          }
+        }
+      }
+    }
+    // retrieve database refs for protein sequences
+    if (!seqsWithoutSourceDBRef.isEmpty())
+    {
+      canQueryTDB = true;
+      if (needCanonical)
+      {
+        notQueriedTDBYet = false;
+      }
+    }
+  };
+
+  /**
    * Initializes parameters used by the Structure Chooser Panel
    */
   protected void init()
@@ -114,31 +184,161 @@ public class StructureChooser extends GStructureChooser
     }
 
     chk_superpose.setSelected(Cache.getDefault(AUTOSUPERIMPOSE, true));
+    btn_queryTDB.addActionListener(new ActionListener()
+    {
+
+      @Override
+      public void actionPerformed(ActionEvent e)
+      {
+        promptForTDBFetch();
+      }
+    });
+
+    Executors.defaultThreadFactory().newThread(new Runnable()
+    {
+      public void run()
+      {
+        populateSeqsWithoutSourceDBRef();
+        initialStructureDiscovery();
+      }
+
+    }).start();
+
+  }
+
+  // called by init
+  private void initialStructureDiscovery()
+  {
+    // check which FTS engine to use
+    data = StructureChooserQuerySource.getQuerySourceFor(selectedSequences);
 
     // ensure a filter option is in force for search
     populateFilterComboBox(true, cachedPDBExists);
-    Thread discoverPDBStructuresThread = new Thread(new Runnable()
+
+    // looks for any existing structures already loaded
+    // for the sequences (the cached ones)
+    // then queries the StructureChooserQuerySource to
+    // discover more structures.
+    //
+    // Possible optimisation is to only begin querying
+    // the structure chooser if there are no cached structures.
+
+    long startTime = System.currentTimeMillis();
+    updateProgressIndicator(
+            MessageManager.getString("status.loading_cached_pdb_entries"),
+            startTime);
+    loadLocalCachedPDBEntries();
+    updateProgressIndicator(null, startTime);
+    updateProgressIndicator(
+            MessageManager.getString("status.searching_for_pdb_structures"),
+            startTime);
+    fetchStructuresMetaData();
+    // revise filter options if no results were found
+    populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
+    discoverStructureViews();
+    updateProgressIndicator(null, startTime);
+    mainFrame.setVisible(true);
+    updateCurrentView();
+  }
+
+  private void promptForTDBFetch()
+  {
+    final long progressId = System.currentTimeMillis();
+
+    // final action after prompting and discovering db refs
+    final Runnable strucDiscovery = new Runnable()
     {
       @Override
       public void run()
       {
-        long startTime = System.currentTimeMillis();
-        updateProgressIndicator(MessageManager
-                .getString("status.loading_cached_pdb_entries"), startTime);
-        loadLocalCachedPDBEntries();
-        updateProgressIndicator(null, startTime);
-        updateProgressIndicator(MessageManager.getString(
-                "status.searching_for_pdb_structures"), startTime);
-        fetchStructuresMetaData();
-        // revise filter options if no results were found
-        populateFilterComboBox(isStructuresDiscovered(), cachedPDBExists);
-        discoverStructureViews();
-        updateProgressIndicator(null, startTime);
-        mainFrame.setVisible(true);
-        updateCurrentView();
+        mainFrame.setEnabled(false);
+        cmb_filterOption.setEnabled(false);
+        progressBar.setProgressBar(MessageManager.getString("status.searching_3d_beacons"), progressId);
+        // TODO: warn if no accessions discovered
+        populateSeqsWithoutSourceDBRef();
+        // redo initial discovery - this time with 3d beacons
+        // Executors.
+        previousWantedFields=null;
+        initialStructureDiscovery();
+        if (!isStructuresDiscovered())
+        {
+          progressBar.setProgressBar(MessageManager.getString("status.no_structures_discovered_from_3d_beacons"), progressId);
+          btn_queryTDB.setToolTipText(MessageManager.getString("status.no_structures_discovered_from_3d_beacons"));
+          btn_queryTDB.setEnabled(false);
+        } else {
+          btn_queryTDB.setVisible(false);
+          progressBar.setProgressBar(null, progressId);
+        }
+        mainFrame.setEnabled(true);
+        cmb_filterOption.setEnabled(true);
       }
-    });
-    discoverPDBStructuresThread.start();
+    };
+
+    final FetchFinishedListenerI afterDbRefFetch = new FetchFinishedListenerI()
+    {
+      
+      @Override
+      public void finished()
+      {
+        // filter has been selected, so we set flag to remove ourselves
+        notQueriedTDBYet = false;
+        // new thread to discover structures - via 3d beacons
+        Executors.defaultThreadFactory().newThread(strucDiscovery).start();
+        
+      }
+    };
+    
+    // fetch db refs if OK pressed
+    final Runnable discoverCanonicalDBrefs = new Runnable() 
+    {
+      @Override
+      public void run()
+      {
+        populateSeqsWithoutSourceDBRef();
+
+        final int y = seqsWithoutSourceDBRef.size();
+        if (y > 0)
+        {
+          final SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
+                  .toArray(new SequenceI[y]);
+          DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef,
+                  progressBar, new DbSourceProxy[]
+                  { new jalview.ws.dbsources.Uniprot() }, null, false);
+          dbRefFetcher.addListener(afterDbRefFetch);
+          // ideally this would also gracefully run with callbacks
+          dbRefFetcher.fetchDBRefs(true);
+        } else {
+          // call finished action directly
+          afterDbRefFetch.finished();
+        }
+      }
+
+    };
+    final Runnable revertview = new Runnable() {
+      public void run() {
+        if (lastSelected!=null) {
+          cmb_filterOption.setSelectedItem(lastSelected);
+        }
+      };
+    };
+    // need cancel and no to result in the discoverPDB action - mocked is
+    // 'cancel'
+    JvOptionPane.newOptionDialog(this)
+            .setResponseHandler(JvOptionPane.OK_OPTION,
+                    discoverCanonicalDBrefs)
+            .setResponseHandler(JvOptionPane.CANCEL_OPTION, revertview)
+            .setResponseHandler(JvOptionPane.NO_OPTION, revertview)
+            .showDialog(
+                    MessageManager.formatMessage(
+                            "label.fetch_references_for_3dbeacons",
+                            seqsWithoutSourceDBRef.size()),
+                    MessageManager
+                            .getString("label.3dbeacons"),
+                    JvOptionPane.YES_NO_OPTION, JvOptionPane.PLAIN_MESSAGE,
+                    null, new Object[]
+                    { MessageManager.getString("action.ok"),
+                        MessageManager.getString("action.cancel") },
+                    MessageManager.getString("action.ok"));
   }
 
   /**
@@ -165,8 +365,7 @@ public class StructureChooser extends GStructureChooser
 
         if (view.isLinkedWith(ap))
         {
-          targetView.insertItemAt(viewHandler,
-                  linkedViewsAt++);
+          targetView.insertItemAt(viewHandler, linkedViewsAt++);
         }
         else
         {
@@ -217,36 +416,36 @@ public class StructureChooser extends GStructureChooser
   void fetchStructuresMetaData()
   {
     long startTime = System.currentTimeMillis();
-    pdbRestClient = PDBFTSRestClient.getInstance();
-    Collection<FTSDataColumnI> wantedFields = pdbDocFieldPrefs
+    Collection<FTSDataColumnI> wantedFields = data.getDocFieldPrefs()
             .getStructureSummaryFields();
 
     discoveredStructuresSet = new LinkedHashSet<>();
     HashSet<String> errors = new HashSet<>();
+
+    FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+            .getSelectedItem());
+
     for (SequenceI seq : selectedSequences)
     {
-      FTSRestRequest pdbRequest = new FTSRestRequest();
-      pdbRequest.setAllowEmptySeq(false);
-      pdbRequest.setResponseSize(500);
-      pdbRequest.setFieldToSearchBy("(");
-      FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
-              .getSelectedItem());
-      pdbRequest.setFieldToSortBy(selectedFilterOpt.getValue(),
-              !chk_invertFilter.isSelected());
-      pdbRequest.setWantedFields(wantedFields);
-      pdbRequest.setSearchTerm(buildQuery(seq) + ")");
-      pdbRequest.setAssociatedSequence(seq);
+
       FTSRestResponse resultList;
       try
       {
-        resultList = pdbRestClient.executeRequest(pdbRequest);
+        resultList = data.fetchStructuresMetaData(seq, wantedFields,
+                selectedFilterOpt, !chk_invertFilter.isSelected());
+        // null response means the FTSengine didn't yield a query for this
+        // consider designing a special exception if we really wanted to be
+        // OOCrazy
+        if (resultList == null)
+        {
+          continue;
+        }
       } catch (Exception e)
       {
         e.printStackTrace();
         errors.add(e.getMessage());
         continue;
       }
-      lastPdbRequest = pdbRequest;
       if (resultList.getSearchSummary() != null
               && !resultList.getSearchSummary().isEmpty())
       {
@@ -260,9 +459,11 @@ public class StructureChooser extends GStructureChooser
     if (discoveredStructuresSet != null
             && !discoveredStructuresSet.isEmpty())
     {
-      getResultTable().setModel(FTSRestResponse
-              .getTableModel(lastPdbRequest, discoveredStructuresSet));
+      getResultTable()
+              .setModel(data.getTableModel(discoveredStructuresSet));
+
       noOfStructuresFound = discoveredStructuresSet.size();
+      lastDiscoveredStructuresSet = discoveredStructuresSet;
       mainFrame.setTitle(MessageManager.formatMessage(
               "label.structure_chooser_no_of_structures",
               noOfStructuresFound, totalTime));
@@ -309,157 +510,6 @@ public class StructureChooser extends GStructureChooser
   }
 
   /**
-   * Builds a query string for a given sequences using its DBRef entries
-   * 
-   * @param seq
-   *          the sequences to build a query for
-   * @return the built query string
-   */
-
-  static String buildQuery(SequenceI seq)
-  {
-    boolean isPDBRefsFound = false;
-    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)
-    {
-      for (PDBEntry entry : seq.getAllPDBEntries())
-      {
-        if (isValidSeqName(entry.getId()))
-        {
-          String id = entry.getId().toLowerCase();
-          queryBuilder.append("pdb_id:").append(id).append(" OR ");
-          isPDBRefsFound = true;
-          pdbids.add(id);
-        }
-      }
-    }
-
-    List<DBRefEntry> refs = seq.getDBRefs();
-    if (refs != null && refs.size() != 0)
-    {
-      for (int ib = 0, nb = refs.size(); ib < nb; ib++)
-      {
-         DBRefEntry dbRef = refs.get(ib);
-        if (isValidSeqName(getDBRefId(dbRef))
-                && queryBuilder.length() < MAX_QLENGTH)
-        {
-          if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
-          {
-            queryBuilder.append("uniprot_accession:")
-                    .append(getDBRefId(dbRef)).append(" OR ");
-            queryBuilder.append("uniprot_id:").append(getDBRefId(dbRef))
-                    .append(" OR ");
-            isUniProtRefsFound = true;
-          }
-          else if (dbRef.getSource().equalsIgnoreCase(DBRefSource.PDB))
-          {
-
-            String id = getDBRefId(dbRef).toLowerCase();
-            if (!pdbids.contains(id))
-            {
-              queryBuilder.append("pdb_id:").append(id).append(" OR ");
-              isPDBRefsFound = true;
-              pdbids.add(id);
-            }
-          }
-          else
-          {
-            seqRefs.add(getDBRefId(dbRef));
-          }
-        }
-      }
-    }
-
-    if (!isPDBRefsFound && !isUniProtRefsFound)
-    {
-      String seqName = seq.getName();
-      seqName = sanitizeSeqName(seqName);
-      String[] names = seqName.toLowerCase().split("\\|");
-      for (String name : names)
-      {
-        // System.out.println("Found name : " + name);
-        name.trim();
-        if (isValidSeqName(name))
-        {
-          seqRefs.add(name);
-        }
-      }
-
-      for (String seqRef : seqRefs)
-      {
-        queryBuilder.append("text:").append(seqRef).append(" OR ");
-      }
-    }
-
-    int endIndex = queryBuilder.lastIndexOf(" OR ");
-    if (queryBuilder.toString().length() < 6)
-    {
-      return null;
-    }
-    String query = queryBuilder.toString().substring(0, endIndex);
-    return query;
-  }
-
-  /**
-   * Remove the following special characters from input string +, -, &, !, (, ),
-   * {, }, [, ], ^, ", ~, *, ?, :, \
-   * 
-   * @param seqName
-   * @return
-   */
-  static String sanitizeSeqName(String seqName)
-  {
-    Objects.requireNonNull(seqName);
-    return seqName.replaceAll("\\[\\d*\\]", "")
-            .replaceAll("[^\\dA-Za-z|_]", "").replaceAll("\\s+", "+");
-  }
-
-  /**
-   * Ensures sequence ref names are not less than 3 characters and does not
-   * contain a database name
-   * 
-   * @param seqName
-   * @return
-   */
-  static boolean isValidSeqName(String seqName)
-  {
-    // System.out.println("seqName : " + seqName);
-    String ignoreList = "pdb,uniprot,swiss-prot";
-    if (seqName.length() < 3)
-    {
-      return false;
-    }
-    if (seqName.contains(":"))
-    {
-      return false;
-    }
-    seqName = seqName.toLowerCase();
-    for (String ignoredEntry : ignoreList.split(","))
-    {
-      if (seqName.contains(ignoredEntry))
-      {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  static String getDBRefId(DBRefEntry dbRef)
-  {
-    String ref = dbRef.getAccessionId().replaceAll("GO:", "");
-    return ref;
-  }
-
-  /**
    * Filters a given list of discovered structures based on supplied argument
    * 
    * @param fieldToFilterBy
@@ -469,54 +519,33 @@ public class StructureChooser extends GStructureChooser
   {
     Thread filterThread = new Thread(new Runnable()
     {
+
       @Override
       public void run()
       {
         long startTime = System.currentTimeMillis();
-        pdbRestClient = PDBFTSRestClient.getInstance();
         lbl_loading.setVisible(true);
-        Collection<FTSDataColumnI> wantedFields = pdbDocFieldPrefs
+        Collection<FTSDataColumnI> wantedFields = data.getDocFieldPrefs()
                 .getStructureSummaryFields();
         Collection<FTSData> filteredResponse = new HashSet<>();
         HashSet<String> errors = new HashSet<>();
 
         for (SequenceI seq : selectedSequences)
         {
-          FTSRestRequest pdbRequest = new FTSRestRequest();
-          if (fieldToFilterBy.equalsIgnoreCase("uniprot_coverage"))
-          {
-            pdbRequest.setAllowEmptySeq(false);
-            pdbRequest.setResponseSize(1);
-            pdbRequest.setFieldToSearchBy("(");
-            pdbRequest.setSearchTerm(buildQuery(seq) + ")");
-            pdbRequest.setWantedFields(wantedFields);
-            pdbRequest.setAssociatedSequence(seq);
-            pdbRequest.setFacet(true);
-            pdbRequest.setFacetPivot(fieldToFilterBy + ",entry_entity");
-            pdbRequest.setFacetPivotMinCount(1);
-          }
-          else
-          {
-            pdbRequest.setAllowEmptySeq(false);
-            pdbRequest.setResponseSize(1);
-            pdbRequest.setFieldToSearchBy("(");
-            pdbRequest.setFieldToSortBy(fieldToFilterBy,
-                    !chk_invertFilter.isSelected());
-            pdbRequest.setSearchTerm(buildQuery(seq) + ")");
-            pdbRequest.setWantedFields(wantedFields);
-            pdbRequest.setAssociatedSequence(seq);
-          }
+
           FTSRestResponse resultList;
           try
           {
-            resultList = pdbRestClient.executeRequest(pdbRequest);
+            resultList = data.selectFirstRankedQuery(seq,
+                    discoveredStructuresSet, wantedFields, fieldToFilterBy,
+                    !chk_invertFilter.isSelected());
+
           } catch (Exception e)
           {
             e.printStackTrace();
             errors.add(e.getMessage());
             continue;
           }
-          lastPdbRequest = pdbRequest;
           if (resultList.getSearchSummary() != null
                   && !resultList.getSearchSummary().isEmpty())
           {
@@ -532,8 +561,8 @@ public class StructureChooser extends GStructureChooser
           Collection<FTSData> reorderedStructuresSet = new LinkedHashSet<>();
           reorderedStructuresSet.addAll(filteredResponse);
           reorderedStructuresSet.addAll(discoveredStructuresSet);
-          getResultTable().setModel(FTSRestResponse
-                  .getTableModel(lastPdbRequest, reorderedStructuresSet));
+          getResultTable()
+                  .setModel(data.getTableModel(reorderedStructuresSet));
 
           FTSRestResponse.configureTableColumn(getResultTable(),
                   wantedFields, tempUserPrefs);
@@ -577,7 +606,8 @@ public class StructureChooser extends GStructureChooser
   @Override
   protected void pdbFromFile_actionPerformed()
   {
-    // TODO: JAL-3048 not needed for Jalview-JS until JSmol dep and StructureChooser
+    // TODO: JAL-3048 not needed for Jalview-JS until JSmol dep and
+    // StructureChooser
     // works
     jalview.io.JalviewFileChooser chooser = new jalview.io.JalviewFileChooser(
             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
@@ -605,46 +635,67 @@ public class StructureChooser extends GStructureChooser
   protected void populateFilterComboBox(boolean haveData,
           boolean cachedPDBExist)
   {
+    populateFilterComboBox(haveData, cachedPDBExist, null);
+  }
+
+  /**
+   * Populates the filter combo-box options dynamically depending on discovered
+   * structures
+   */
+  protected void populateFilterComboBox(boolean haveData,
+          boolean cachedPDBExist, FilterOption lastSel)
+  {
+
     /*
      * temporarily suspend the change listener behaviour
      */
     cmb_filterOption.removeItemListener(this);
-
+    int selSet = -1;
     cmb_filterOption.removeAllItems();
     if (haveData)
     {
-      cmb_filterOption.addItem(new FilterOption(
-              MessageManager.getString("label.best_quality"),
-              "overall_quality", VIEWS_FILTER, false));
-      cmb_filterOption.addItem(new FilterOption(
-              MessageManager.getString("label.best_resolution"),
-              "resolution", VIEWS_FILTER, false));
-      cmb_filterOption.addItem(new FilterOption(
-              MessageManager.getString("label.most_protein_chain"),
-              "number_of_protein_chains", VIEWS_FILTER, false));
-      cmb_filterOption.addItem(new FilterOption(
-              MessageManager.getString("label.most_bound_molecules"),
-              "number_of_bound_molecules", VIEWS_FILTER, false));
-      cmb_filterOption.addItem(new FilterOption(
-              MessageManager.getString("label.most_polymer_residues"),
-              "number_of_polymer_residues", VIEWS_FILTER, true));
+      List<FilterOption> filters = data
+              .getAvailableFilterOptions(VIEWS_FILTER);
+      data.updateAvailableFilterOptions(VIEWS_FILTER, filters,
+              lastDiscoveredStructuresSet);
+      int p = 0;
+      for (FilterOption filter : filters)
+      {
+        if (lastSel != null && filter.equals(lastSel))
+        {
+          selSet = p;
+        }
+        p++;
+        cmb_filterOption.addItem(filter);
+      }
     }
+
     cmb_filterOption.addItem(
             new FilterOption(MessageManager.getString("label.enter_pdb_id"),
-                    "-", VIEWS_ENTER_ID, false));
+                    "-", VIEWS_ENTER_ID, false, null));
     cmb_filterOption.addItem(
             new FilterOption(MessageManager.getString("label.from_file"),
-                    "-", VIEWS_FROM_FILE, false));
+                    "-", VIEWS_FROM_FILE, false, null));
+    if (canQueryTDB && notQueriedTDBYet)
+    {
+      btn_queryTDB.setVisible(true);
+    }
 
     if (cachedPDBExist)
     {
       FilterOption cachedOption = new FilterOption(
-              MessageManager.getString("label.cached_structures"),
-              "-", VIEWS_LOCAL_PDB, false);
+              MessageManager.getString("label.cached_structures"), "-",
+              VIEWS_LOCAL_PDB, false, null);
       cmb_filterOption.addItem(cachedOption);
-      cmb_filterOption.setSelectedItem(cachedOption);
+      if (selSet == -1)
+      {
+        cmb_filterOption.setSelectedItem(cachedOption);
+      }
+    }
+    if (selSet > -1)
+    {
+      cmb_filterOption.setSelectedIndex(selSet);
     }
-
     cmb_filterOption.addItemListener(this);
   }
 
@@ -655,16 +706,41 @@ public class StructureChooser extends GStructureChooser
   {
     FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
             .getSelectedItem());
+    
+    if (lastSelected == selectedFilterOpt)
+    {
+      // don't need to do anything, probably
+      return;
+    }
+    // otherwise, record selection
+    // and update the layout and dialog accordingly
+    lastSelected = selectedFilterOpt;
+
     layout_switchableViews.show(pnl_switchableViews,
             selectedFilterOpt.getView());
     String filterTitle = mainFrame.getTitle();
     mainFrame.setTitle(frameTitle);
     chk_invertFilter.setVisible(false);
+    
     if (selectedFilterOpt.getView() == VIEWS_FILTER)
     {
       mainFrame.setTitle(filterTitle);
-      chk_invertFilter.setVisible(true);
-      filterResultSet(selectedFilterOpt.getValue());
+      // TDB Query has no invert as yet
+      chk_invertFilter.setVisible(selectedFilterOpt
+              .getQuerySource() instanceof PDBStructureChooserQuerySource);
+
+      if (data != selectedFilterOpt.getQuerySource()
+              || data.needsRefetch(selectedFilterOpt))
+      {
+        data = selectedFilterOpt.getQuerySource();
+        // rebuild the views completely, since prefs will also change
+        tabRefresh();
+        return;
+      }
+      else
+      {
+        filterResultSet(selectedFilterOpt.getValue());
+      }
     }
     else if (selectedFilterOpt.getView() == VIEWS_ENTER_ID
             || selectedFilterOpt.getView() == VIEWS_FROM_FILE)
@@ -730,6 +806,38 @@ public class StructureChooser extends GStructureChooser
             .setEnabled(selectedCount > 1 || targetView.getItemCount() > 0);
   }
 
+  @Override
+  protected boolean showPopupFor(int selectedRow, int x, int y)
+  {
+    FilterOption selectedFilterOpt = ((FilterOption) cmb_filterOption
+            .getSelectedItem());
+    String currentView = selectedFilterOpt.getView();
+     
+    if (currentView == VIEWS_FILTER && data instanceof ThreeDBStructureChooserQuerySource)
+    {
+      
+      TDB_FTSData row=((ThreeDBStructureChooserQuerySource)data).getFTSDataFor(getResultTable(), selectedRow, discoveredStructuresSet);
+      String pageUrl = row.getModelViewUrl(); 
+      JPopupMenu popup = new JPopupMenu("3D Beacons");
+      JMenuItem viewUrl = new JMenuItem("View model web page");
+      viewUrl.addActionListener(
+              new ActionListener() {
+                @Override
+                public void actionPerformed(ActionEvent e)
+                {
+                  Desktop.showUrl(pageUrl);
+                }
+              }
+              );
+      popup.add(viewUrl);
+      SwingUtilities.invokeLater(new Runnable()  {
+        public void run() { popup.show(getResultTable(), x, y); }
+      });
+      return true;
+    }
+    // event not handled by us
+    return false;
+  }
   /**
    * Validates inputs from the Manual PDB entry panel
    */
@@ -801,7 +909,7 @@ public class StructureChooser extends GStructureChooser
   {
     validateSelections();
   }
-
+  private FilterOption lastSelected=null;
   /**
    * Handles the state change event for the 'filter' combo-box and 'invert'
    * check-box
@@ -861,7 +969,7 @@ public class StructureChooser extends GStructureChooser
     }
     return found;
   }
-  
+
   /**
    * Handles the 'New View' action
    */
@@ -906,37 +1014,12 @@ public class StructureChooser extends GStructureChooser
 
         if (currentView == VIEWS_FILTER)
         {
-          int pdbIdColIndex = restable.getColumn("PDB Id")
-                  .getModelIndex();
-          int refSeqColIndex = restable.getColumn("Ref Sequence")
-                  .getModelIndex();
           int[] selectedRows = restable.getSelectedRows();
           PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
-          int count = 0;
           List<SequenceI> selectedSeqsToView = new ArrayList<>();
-          for (int row : selectedRows)
-          {
-            String pdbIdStr = restable
-                    .getValueAt(row, pdbIdColIndex).toString();
-            SequenceI selectedSeq = (SequenceI) restable
-                    .getValueAt(row, refSeqColIndex);
-            selectedSeqsToView.add(selectedSeq);
-            PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
-            if (pdbEntry == null)
-            {
-              pdbEntry = getFindEntry(pdbIdStr,
-                      selectedSeq.getAllPDBEntries());
-            }
+          pdbEntriesToView = data.collectSelectedRows(restable,
+                  selectedRows, selectedSeqsToView);
 
-            if (pdbEntry == null)
-            {
-              pdbEntry = new PDBEntry();
-              pdbEntry.setId(pdbIdStr);
-              pdbEntry.setType(PDBEntry.Type.PDB);
-              selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
-            }
-            pdbEntriesToView[count++] = pdbEntry;
-          }
           SequenceI[] selectedSeqs = selectedSeqsToView
                   .toArray(new SequenceI[selectedSeqsToView.size()]);
           sViewer = launchStructureViewer(ssm, pdbEntriesToView, ap,
@@ -954,8 +1037,9 @@ public class StructureChooser extends GStructureChooser
           List<SequenceI> selectedSeqsToView = new ArrayList<>();
           for (int row : selectedRows)
           {
-            PDBEntry pdbEntry = (PDBEntry) tbl_local_pdb.getValueAt(row,
-                    pdbIdColIndex);
+            PDBEntry pdbEntry = ((PDBEntryTableModel) tbl_local_pdb
+                    .getModel()).getPDBEntryAt(row).getPdbEntry();
+
             pdbEntriesToView[count++] = pdbEntry;
             SequenceI selectedSeq = (SequenceI) tbl_local_pdb
                     .getValueAt(row, refSeqColIndex);
@@ -982,7 +1066,7 @@ public class StructureChooser extends GStructureChooser
             if (pdbIdStr.split(":").length > 1)
             {
               pdbEntry.setId(pdbIdStr.split(":")[0]);
-              pdbEntry.setChainCode(pdbIdStr.split(":")[1].toUpperCase());
+              pdbEntry.setChainCode(pdbIdStr.split(":")[1].toUpperCase(Locale.ROOT));
             }
             else
             {
@@ -1010,10 +1094,8 @@ public class StructureChooser extends GStructureChooser
                           DataSourceType.FILE, selectedSequence, true,
                           Desktop.instance);
 
-          sViewer = launchStructureViewer(
-                  ssm, new PDBEntry[]
-                  { fileEntry }, ap,
-                  new SequenceI[]
+          sViewer = launchStructureViewer(ssm, new PDBEntry[] { fileEntry },
+                  ap, new SequenceI[]
                   { selectedSequence });
         }
         SwingUtilities.invokeLater(new Runnable()
@@ -1046,21 +1128,6 @@ public class StructureChooser extends GStructureChooser
     }
   }
 
-  private PDBEntry getFindEntry(String id, Vector<PDBEntry> pdbEntries)
-  {
-    Objects.requireNonNull(id);
-    Objects.requireNonNull(pdbEntries);
-    PDBEntry foundEntry = null;
-    for (PDBEntry entry : pdbEntries)
-    {
-      if (entry.getId().equalsIgnoreCase(id))
-      {
-        return entry;
-      }
-    }
-    return foundEntry;
-  }
-
   /**
    * Answers a structure viewer (new or existing) configured to superimpose
    * added structures or not according to the user's choice
@@ -1068,8 +1135,7 @@ public class StructureChooser extends GStructureChooser
    * @param ssm
    * @return
    */
-  StructureViewer getTargetedStructureViewer(
-          StructureSelectionManager ssm)
+  StructureViewer getTargetedStructureViewer(StructureSelectionManager ssm)
   {
     Object sv = targetView.getSelectedItem();
 
@@ -1086,8 +1152,7 @@ public class StructureChooser extends GStructureChooser
    * @return
    */
   private StructureViewer launchStructureViewer(
-          StructureSelectionManager ssm,
-          final PDBEntry[] pdbEntriesToView,
+          StructureSelectionManager ssm, final PDBEntry[] pdbEntriesToView,
           final AlignmentPanel alignPanel, SequenceI[] sequences)
   {
     long progressId = sequences.hashCode();
@@ -1150,8 +1215,9 @@ public class StructureChooser extends GStructureChooser
     }
     if (pdbEntriesToView.length > 1)
     {
-      setProgressBar(MessageManager.getString(
-              "status.fetching_3d_structures_for_selected_entries"),
+      setProgressBar(
+              MessageManager.getString(
+                      "status.fetching_3d_structures_for_selected_entries"),
               progressId);
       theViewer.viewStructures(pdbEntriesToView, sequences, alignPanel);
     }
@@ -1159,7 +1225,7 @@ public class StructureChooser extends GStructureChooser
     {
       setProgressBar(MessageManager.formatMessage(
               "status.fetching_3d_structures_for",
-              pdbEntriesToView[0].getId()),progressId);
+              pdbEntriesToView[0].getId()), progressId);
       theViewer.viewStructures(pdbEntriesToView[0], sequences, alignPanel);
     }
     setProgressBar(null, progressId);
@@ -1204,7 +1270,8 @@ public class StructureChooser extends GStructureChooser
             && !discoveredStructuresSet.isEmpty();
   }
 
-  protected int PDB_ID_MIN = 3;// or: (Jalview.isJS() ? 3 : 1); // Bob proposes this. 
+  protected int PDB_ID_MIN = 3;// or: (Jalview.isJS() ? 3 : 1); // Bob proposes
+                               // this.
   // Doing a search for "1" or "1c" is valuable?
   // Those work but are enormously slow.
 
@@ -1212,51 +1279,54 @@ public class StructureChooser extends GStructureChooser
   protected void txt_search_ActionPerformed()
   {
     String text = txt_search.getText().trim();
-       if (text.length() >= PDB_ID_MIN) 
-    new Thread()
-    {
-
-       @Override
-      public void run()
+    if (text.length() >= PDB_ID_MIN)
+      new Thread()
       {
-        errorWarning.setLength(0);
-        isValidPBDEntry = false;
-        if (text.length() > 0)
+
+        @Override
+        public void run()
         {
-          String searchTerm = text.toLowerCase();
-          searchTerm = searchTerm.split(":")[0];
-          // System.out.println(">>>>> search term : " + searchTerm);
-          List<FTSDataColumnI> wantedFields = new ArrayList<>();
-          FTSRestRequest pdbRequest = new FTSRestRequest();
-          pdbRequest.setAllowEmptySeq(false);
-          pdbRequest.setResponseSize(1);
-          pdbRequest.setFieldToSearchBy("(pdb_id:");
-          pdbRequest.setWantedFields(wantedFields);
-          pdbRequest.setSearchTerm(searchTerm + ")");
-          pdbRequest.setAssociatedSequence(selectedSequence);
-          pdbRestClient = PDBFTSRestClient.getInstance();
-          wantedFields.add(pdbRestClient.getPrimaryKeyColumn());
-          FTSRestResponse resultList;
-          try
-          {
-            resultList = pdbRestClient.executeRequest(pdbRequest);
-          } catch (Exception e)
-          {
-            errorWarning.append(e.getMessage());
-            return;
-          } finally
-          {
-            validateSelections();
-          }
-          if (resultList.getSearchSummary() != null
-                  && resultList.getSearchSummary().size() > 0)
+          errorWarning.setLength(0);
+          isValidPBDEntry = false;
+          if (text.length() > 0)
           {
-            isValidPBDEntry = true;
+            // TODO move this pdb id search into the PDB specific
+            // FTSSearchEngine
+            // for moment, it will work fine as is because it is self-contained
+            String searchTerm = text.toLowerCase(Locale.ROOT);
+            searchTerm = searchTerm.split(":")[0];
+            // System.out.println(">>>>> search term : " + searchTerm);
+            List<FTSDataColumnI> wantedFields = new ArrayList<>();
+            FTSRestRequest pdbRequest = new FTSRestRequest();
+            pdbRequest.setAllowEmptySeq(false);
+            pdbRequest.setResponseSize(1);
+            pdbRequest.setFieldToSearchBy("(pdb_id:");
+            pdbRequest.setWantedFields(wantedFields);
+            pdbRequest.setSearchTerm(searchTerm + ")");
+            pdbRequest.setAssociatedSequence(selectedSequence);
+            FTSRestClientI pdbRestClient = PDBFTSRestClient.getInstance();
+            wantedFields.add(pdbRestClient.getPrimaryKeyColumn());
+            FTSRestResponse resultList;
+            try
+            {
+              resultList = pdbRestClient.executeRequest(pdbRequest);
+            } catch (Exception e)
+            {
+              errorWarning.append(e.getMessage());
+              return;
+            } finally
+            {
+              validateSelections();
+            }
+            if (resultList.getSearchSummary() != null
+                    && resultList.getSearchSummary().size() > 0)
+            {
+              isValidPBDEntry = true;
+            }
           }
+          validateSelections();
         }
-        validateSelections();
-      }
-    }.start();
+      }.start();
   }
 
   @Override
@@ -1270,6 +1340,8 @@ public class StructureChooser extends GStructureChooser
         public void run()
         {
           fetchStructuresMetaData();
+          // populateFilterComboBox(true, cachedPDBExists);
+
           filterResultSet(
                   ((FilterOption) cmb_filterOption.getSelectedItem())
                           .getValue());
@@ -1326,7 +1398,7 @@ public class StructureChooser extends GStructureChooser
         value = entry.getSequence();
         break;
       case 1:
-        value = entry.getPdbEntry();
+        value = entry.getQualifiedId();
         break;
       case 2:
         value = entry.getPdbEntry().getChainCode() == null ? "_"
@@ -1367,6 +1439,15 @@ public class StructureChooser extends GStructureChooser
       this.pdbEntry = pdbEntry;
     }
 
+    public String getQualifiedId()
+    {
+      if (pdbEntry.hasProvider())
+      {
+        return pdbEntry.getProvider() + ":" + pdbEntry.getId();
+      }
+      return pdbEntry.toString();
+    }
+
     public SequenceI getSequence()
     {
       return sequence;
@@ -1403,4 +1484,23 @@ public class StructureChooser extends GStructureChooser
   {
     return sViewer == null ? null : sViewer.sview;
   }
+
+  @Override
+  protected void setFTSDocFieldPrefs(FTSDataColumnPreferences newPrefs)
+  {
+    data.setDocFieldPrefs(newPrefs);
+
+  }
+
+  /**
+   * 
+   * @return true when all initialisation threads have finished and dialog is
+   *         visible
+   */
+  public boolean isDialogVisible()
+  {
+    return mainFrame != null && data != null && cmb_filterOption != null
+            && mainFrame.isVisible()
+            && cmb_filterOption.getSelectedItem() != null;
+  }
 }
index 617706a..7b1864b 100644 (file)
@@ -80,6 +80,7 @@ public class StructureViewer
     return sv;
   }
 
+  
   @Override
   public String toString()
   {
@@ -89,7 +90,11 @@ public class StructureViewer
     }
     return "New View";
   }
-  public ViewerType getViewerType()
+  /**
+   * 
+   * @return ViewerType for currently configured structure viewer 
+   */
+  public static ViewerType getViewerType()
   {
     String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY,
             ViewerType.JMOL.name());
@@ -366,6 +371,7 @@ public class StructureViewer
     return viewer;
   }
 
+
   public boolean isBusy()
   {
     if (sview != null)
index a0b199b..021e2f6 100644 (file)
@@ -63,7 +63,9 @@ import jalview.structure.StructureMapping;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.BrowserLauncher;
 import jalview.util.MessageManager;
+import jalview.ws.dbsources.EBIAlfaFold;
 import jalview.ws.dbsources.Pdb;
+import jalview.ws.utils.UrlDownloadClient;
 
 /**
  * Base class with common functionality for JMol, Chimera or other structure
@@ -153,6 +155,15 @@ public abstract class StructureViewerBase extends GStructureViewer
   {
     alignAddedStructures = alignAdded;
   }
+  
+  /**
+   * called by the binding model to indicate when adding structures is happening or has been completed
+   * @param addingStructures
+   */
+  public synchronized void setAddingStructures(boolean addingStructures)
+  {
+    this.addingStructures = addingStructures;
+  }
 
   /**
    * 
@@ -1076,7 +1087,7 @@ public abstract class StructureViewerBase extends GStructureViewer
     progressBar = pi;
   }
 
-  protected void setProgressMessage(String message, long id)
+  public void setProgressMessage(String message, long id)
   {
     if (progressBar != null)
     {
@@ -1121,6 +1132,7 @@ public abstract class StructureViewerBase extends GStructureViewer
   {
     String filePath = null;
     Pdb pdbclient = new Pdb();
+    EBIAlfaFold afclient =  new EBIAlfaFold();
     AlignmentI pdbseq = null;
     String pdbid = processingEntry.getId();
     long handle = System.currentTimeMillis()
@@ -1138,7 +1150,30 @@ public abstract class StructureViewerBase extends GStructureViewer
     // { pdbid }));
     try
     {
-      pdbseq = pdbclient.getSequenceRecords(pdbid);
+      if (afclient.isValidReference(pdbid))
+      {
+        pdbseq = afclient.getSequenceRecords(pdbid);
+      } else {
+          if (processingEntry.hasRetrievalUrl())
+          {
+            // retrieve from URL to new local tmpfile
+            File tmpFile = File.createTempFile(pdbid,
+                    "." + (PDBEntry.Type.MMCIF.toString().equals(
+                            processingEntry.getType().toString()) ? "cif"
+                                    : "pdb"));
+            String fromUrl = processingEntry.getRetrievalUrl();
+            UrlDownloadClient.download(fromUrl, tmpFile);
+            
+            // may not need this check ?
+            String file = tmpFile.getAbsolutePath();
+            if (file != null)
+            {
+              pdbseq = EBIAlfaFold.importDownloadedStructureFromUrl(fromUrl,tmpFile,pdbid,null,null,null);
+            }
+          } else {
+            pdbseq = pdbclient.getSequenceRecords(pdbid);
+          }
+      }
     } catch (Exception e)
     {
       System.err.println(
index 2d8e729..b730df4 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import jalview.analysis.AlignmentSorter;
 import jalview.analysis.AverageDistanceTree;
 import jalview.analysis.NJTree;
@@ -693,7 +695,7 @@ public class TreePanel extends GTreePanel
     String tree = MessageManager.getString("label.tree");
     ImageExporter exporter = new ImageExporter(writer, null, imageFormat,
             tree);
-    exporter.doExport(null, this, width, height, tree.toLowerCase());
+    exporter.doExport(null, this, width, height, tree.toLowerCase(Locale.ROOT));
   }
 
   /**
@@ -723,7 +725,7 @@ public class TreePanel extends GTreePanel
             // search dbrefs, features and annotation
             List<DBRefEntry> refs = jalview.util.DBRefUtils
                     .selectRefs(sq.getDBRefs(), new String[]
-                    { labelClass.toUpperCase() });
+                    { labelClass.toUpperCase(Locale.ROOT) });
             if (refs != null)
             {
               for (int i = 0, ni = refs.size(); i < ni; i++)
@@ -786,7 +788,7 @@ public class TreePanel extends GTreePanel
      * i18n description of Neighbour Joining or Average Distance method
      */
     String treecalcnm = MessageManager
-            .getString("label.tree_calc_" + treeType.toLowerCase());
+            .getString("label.tree_calc_" + treeType.toLowerCase(Locale.ROOT));
 
     /*
      * short score model name (long description can be too long)
index 4846049..c3f132b 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import jalview.bin.Cache;
 import jalview.io.JalviewFileChooser;
 import jalview.io.JalviewFileView;
@@ -205,8 +207,8 @@ public class UserDefinedColours extends GUserDefinedColours
       {
         int row = i / cols + 1;
         int index = (row * cols) + i;
-        JButton button = makeButton(ResidueProperties.aa[i].toLowerCase(),
-                ResidueProperties.aa[i].toLowerCase(), lowerCaseButtons, i);
+        JButton button = makeButton(ResidueProperties.aa[i].toLowerCase(Locale.ROOT),
+                ResidueProperties.aa[i].toLowerCase(Locale.ROOT), lowerCaseButtons, i);
 
         buttonPanel.add(button, index);
       }
index 0773a7b..ee1b473 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.gui;
 
+import java.util.Locale;
+
 import java.awt.BorderLayout;
 import java.awt.Color;
 import java.awt.Dimension;
@@ -545,7 +547,7 @@ public class WebserviceInfo extends GWebserviceInfo
     {
       return null;
     }
-    String lowertxt = text.toLowerCase();
+    String lowertxt = text.toLowerCase(Locale.ROOT);
     int htmlpos = leaveFirst ? -1 : lowertxt.indexOf("<body");
 
     int htmlend = leaveLast ? -1 : lowertxt.indexOf("</body");
@@ -574,7 +576,7 @@ public class WebserviceInfo extends GWebserviceInfo
     {
       return "";
     }
-    String lowertxt = text.toLowerCase();
+    String lowertxt = text.toLowerCase(Locale.ROOT);
     int htmlpos = lowertxt.indexOf("<body");
     int htmlend = lowertxt.indexOf("</body");
     int doctype = lowertxt.indexOf("<!doctype");
index 976e551..7a139e2 100644 (file)
  */
 package jalview.gui;
 
-import jalview.gui.OptsAndParamsPage.OptionBox;
-import jalview.gui.OptsAndParamsPage.ParamBox;
-import jalview.util.MessageManager;
-import jalview.ws.jws2.JabaParamStore;
-import jalview.ws.jws2.JabaPreset;
-import jalview.ws.jws2.Jws2Discoverer;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.ArgumentI;
-import jalview.ws.params.OptionI;
-import jalview.ws.params.ParamDatastoreI;
-import jalview.ws.params.ParameterI;
-import jalview.ws.params.WsParamSetI;
-
 import java.awt.BorderLayout;
 import java.awt.Component;
 import java.awt.Dimension;
@@ -75,6 +62,19 @@ import compbio.metadata.Parameter;
 import compbio.metadata.Preset;
 import compbio.metadata.PresetManager;
 import compbio.metadata.RunnerConfig;
+import jalview.bin.Cache;
+import jalview.gui.OptsAndParamsPage.OptionBox;
+import jalview.gui.OptsAndParamsPage.ParamBox;
+import jalview.util.MessageManager;
+import jalview.ws.jws2.JabaParamStore;
+import jalview.ws.jws2.JabaPreset;
+import jalview.ws.jws2.Jws2Discoverer;
+import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.params.ArgumentI;
+import jalview.ws.params.OptionI;
+import jalview.ws.params.ParamDatastoreI;
+import jalview.ws.params.ParameterI;
+import jalview.ws.params.WsParamSetI;
 import net.miginfocom.swing.MigLayout;
 
 /**
@@ -1339,8 +1339,11 @@ public class WsJobParameters extends JPanel implements ItemListener,
     if (e.getSource() == setName && e.getStateChange() == e.SELECTED)
     {
       final String setname = (String) setName.getSelectedItem();
-      System.out.println("Item state changed for " + setname
-              + " (handling ? " + !settingDialog + ")");
+      if (Cache.log.isDebugEnabled())
+      {
+        Cache.log.debug("Item state changed for " + setname
+                + " (handling ? " + !settingDialog + ")");
+      }
       if (settingDialog)
       {
         // ignore event
diff --git a/src/jalview/gui/structurechooser/PDBStructureChooserQuerySource.java b/src/jalview/gui/structurechooser/PDBStructureChooserQuerySource.java
new file mode 100644 (file)
index 0000000..727d8e0
--- /dev/null
@@ -0,0 +1,373 @@
+package jalview.gui.structurechooser;
+
+import java.util.Locale;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.swing.JTable;
+import javax.swing.table.TableModel;
+
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.jbgui.FilterOption;
+import jalview.util.MessageManager;
+
+/**
+ * logic for querying the PDBe API for structures of sequences
+ * 
+ * @author jprocter
+ */
+public class PDBStructureChooserQuerySource
+        extends StructureChooserQuerySource
+{
+
+  private static int MAX_QLENGTH = 7820;
+
+  protected FTSRestRequest lastPdbRequest;
+
+  protected FTSRestClientI pdbRestClient;
+
+  public PDBStructureChooserQuerySource()
+  {
+    pdbRestClient = PDBFTSRestClient.getInstance();
+    docFieldPrefs = new FTSDataColumnPreferences(
+            PreferenceSource.STRUCTURE_CHOOSER,
+            PDBFTSRestClient.getInstance());
+
+  }
+
+
+  /**
+   * Builds a query string for a given sequences using its DBRef entries
+   * 
+   * @param seq
+   *          the sequences to build a query for
+   * @return the built query string
+   */
+
+  public String buildQuery(SequenceI seq)
+  {
+    boolean isPDBRefsFound = false;
+    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)
+    {
+      for (PDBEntry entry : seq.getAllPDBEntries())
+      {
+        if (isValidSeqName(entry.getId()))
+        {
+          String id = entry.getId().toLowerCase(Locale.ROOT);
+          queryBuilder.append("pdb_id:").append(id).append(" OR ");
+          isPDBRefsFound = true;
+          pdbids.add(id);
+        }
+      }
+    }
+
+    List<DBRefEntry> refs = seq.getDBRefs();
+    if (refs != null && refs.size() != 0)
+    {
+      for (int ib = 0, nb = refs.size(); ib < nb; ib++)
+      {
+        DBRefEntry dbRef = refs.get(ib);
+        if (isValidSeqName(getDBRefId(dbRef))
+                && queryBuilder.length() < MAX_QLENGTH)
+        {
+          if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
+          {
+            queryBuilder.append("uniprot_accession:")
+                    .append(getDBRefId(dbRef)).append(" OR ");
+            queryBuilder.append("uniprot_id:").append(getDBRefId(dbRef))
+                    .append(" OR ");
+            isUniProtRefsFound = true;
+          }
+          else if (dbRef.getSource().equalsIgnoreCase(DBRefSource.PDB))
+          {
+
+            String id = getDBRefId(dbRef).toLowerCase(Locale.ROOT);
+            if (!pdbids.contains(id))
+            {
+              queryBuilder.append("pdb_id:").append(id).append(" OR ");
+              isPDBRefsFound = true;
+              pdbids.add(id);
+            }
+          }
+          else
+          {
+            seqRefs.add(getDBRefId(dbRef));
+          }
+        }
+      }
+    }
+
+    if (!isPDBRefsFound && !isUniProtRefsFound)
+    {
+      String seqName = seq.getName();
+      seqName = sanitizeSeqName(seqName);
+      String[] names = seqName.toLowerCase(Locale.ROOT).split("\\|");
+      for (String name : names)
+      {
+        // System.out.println("Found name : " + name);
+        name.trim();
+        if (isValidSeqName(name))
+        {
+          seqRefs.add(name);
+        }
+      }
+
+      for (String seqRef : seqRefs)
+      {
+        queryBuilder.append("text:").append(seqRef).append(" OR ");
+      }
+    }
+
+    int endIndex = queryBuilder.lastIndexOf(" OR ");
+    if (queryBuilder.toString().length() < 6)
+    {
+      return null;
+    }
+    String query = queryBuilder.toString().substring(0, endIndex);
+    return query;
+  }
+
+  /**
+   * Remove the following special characters from input string +, -, &, !, (, ),
+   * {, }, [, ], ^, ", ~, *, ?, :, \
+   * 
+   * @param seqName
+   * @return
+   */
+  public static String sanitizeSeqName(String seqName)
+  {
+    Objects.requireNonNull(seqName);
+    return seqName.replaceAll("\\[\\d*\\]", "")
+            .replaceAll("[^\\dA-Za-z|_]", "").replaceAll("\\s+", "+");
+  }
+
+  /**
+   * Ensures sequence ref names are not less than 3 characters and does not
+   * contain a database name
+   * 
+   * @param seqName
+   * @return
+   */
+  static boolean isValidSeqName(String seqName)
+  {
+    // System.out.println("seqName : " + seqName);
+    String ignoreList = "pdb,uniprot,swiss-prot";
+    if (seqName.length() < 3)
+    {
+      return false;
+    }
+    if (seqName.contains(":"))
+    {
+      return false;
+    }
+    seqName = seqName.toLowerCase(Locale.ROOT);
+    for (String ignoredEntry : ignoreList.split(","))
+    {
+      if (seqName.contains(ignoredEntry))
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  static String getDBRefId(DBRefEntry dbRef)
+  {
+    String ref = dbRef.getAccessionId().replaceAll("GO:", "");
+    return ref;
+  }
+
+  /**
+   * FTSRestClient specific query builder to recover associated structure data
+   * records for a sequence
+   * 
+   * @param seq
+   *          - seq to generate a query for
+   * @param wantedFields
+   *          - fields to retrieve
+   * @param selectedFilterOpt
+   *          - criterion for ranking results (e.g. resolution)
+   * @param b
+   *          - sort ascending or descending
+   * @return
+   * @throws Exception
+   */
+  public FTSRestResponse fetchStructuresMetaData(SequenceI seq,
+          Collection<FTSDataColumnI> wantedFields,
+          FilterOption selectedFilterOpt, boolean b) throws Exception
+  {
+    FTSRestResponse resultList;
+    FTSRestRequest pdbRequest = new FTSRestRequest();
+    pdbRequest.setAllowEmptySeq(false);
+    pdbRequest.setResponseSize(500);
+    pdbRequest.setFieldToSearchBy("(");
+    pdbRequest.setFieldToSortBy(selectedFilterOpt.getValue(), b);
+    pdbRequest.setWantedFields(wantedFields);
+    pdbRequest.setSearchTerm(buildQuery(seq) + ")");
+    pdbRequest.setAssociatedSequence(seq);
+    resultList = pdbRestClient.executeRequest(pdbRequest);
+
+    lastPdbRequest = pdbRequest;
+    return resultList;
+  }
+  public List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER)
+  {
+    List<FilterOption> filters = new ArrayList<FilterOption>();
+    filters.add(new FilterOption("PDBe "+
+            MessageManager.getString("label.best_quality"),
+            "overall_quality", VIEWS_FILTER, false,this));
+    filters.add(new FilterOption("PDBe "+
+            MessageManager.getString("label.best_resolution"),
+            "resolution", VIEWS_FILTER, false,this));
+    filters.add(new FilterOption("PDBe "+
+            MessageManager.getString("label.most_protein_chain"),
+            "number_of_protein_chains", VIEWS_FILTER, false,this));
+    filters.add(new FilterOption("PDBe "+
+            MessageManager.getString("label.most_bound_molecules"),
+            "number_of_bound_molecules", VIEWS_FILTER, false,this));
+    filters.add(new FilterOption("PDBe "+
+            MessageManager.getString("label.most_polymer_residues"),
+            "number_of_polymer_residues", VIEWS_FILTER, true,this));
+  
+    return filters;
+  }
+
+  @Override
+  public boolean needsRefetch(FilterOption selectedFilterOpt)
+  {
+    // PDBe queries never need a refetch first
+    return false;
+  }
+
+  /**
+   * FTSRestClient specific query builder to pick top ranked entry from a
+   * fetchStructuresMetaData query
+   * 
+   * @param seq
+   *          - seq to generate a query for
+   * @param wantedFields
+   *          - fields to retrieve
+   * @param selectedFilterOpt
+   *          - criterion for ranking results (e.g. resolution)
+   * @param b
+   *          - sort ascending or descending
+   * @return
+   * @throws Exception
+   */
+  public FTSRestResponse selectFirstRankedQuery(SequenceI seq, Collection<FTSData> collectedResults,
+          Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
+          boolean b) throws Exception
+  {
+
+    FTSRestResponse resultList;
+    FTSRestRequest pdbRequest = new FTSRestRequest();
+    if (fieldToFilterBy.equalsIgnoreCase("uniprot_coverage"))
+    {
+      pdbRequest.setAllowEmptySeq(false);
+      pdbRequest.setResponseSize(1);
+      pdbRequest.setFieldToSearchBy("(");
+      pdbRequest.setSearchTerm(buildQuery(seq) + ")");
+      pdbRequest.setWantedFields(wantedFields);
+      pdbRequest.setAssociatedSequence(seq);
+      pdbRequest.setFacet(true);
+      pdbRequest.setFacetPivot(fieldToFilterBy + ",entry_entity");
+      pdbRequest.setFacetPivotMinCount(1);
+    }
+    else
+    {
+      pdbRequest.setAllowEmptySeq(false);
+      pdbRequest.setResponseSize(1);
+      pdbRequest.setFieldToSearchBy("(");
+      pdbRequest.setFieldToSortBy(fieldToFilterBy, b);
+      pdbRequest.setSearchTerm(buildQuery(seq) + ")");
+      pdbRequest.setWantedFields(wantedFields);
+      pdbRequest.setAssociatedSequence(seq);
+    }
+    resultList = pdbRestClient.executeRequest(pdbRequest);
+
+    lastPdbRequest = pdbRequest;
+    return resultList;
+  }
+
+
+  @Override
+  public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows,
+          List<SequenceI> selectedSeqsToView)
+  {
+    int refSeqColIndex = restable.getColumn("Ref Sequence")
+            .getModelIndex();
+
+    PDBEntry[] pdbEntriesToView=new PDBEntry[selectedRows.length];
+    int count = 0;
+    int idColumnIndex=-1;
+    boolean fromTDB=true;
+    idColumnIndex = restable.getColumn("PDB Id").getModelIndex();
+    
+    for (int row : selectedRows)
+    {
+      
+      String pdbIdStr = restable.getValueAt(row,idColumnIndex)
+              .toString();
+      SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
+              refSeqColIndex);
+      selectedSeqsToView.add(selectedSeq);
+      PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
+      if (pdbEntry == null)
+      {
+        pdbEntry = getFindEntry(pdbIdStr,
+                selectedSeq.getAllPDBEntries());
+      }
+
+      if (pdbEntry == null)
+      {
+        pdbEntry = new PDBEntry();
+        pdbEntry.setId(pdbIdStr);
+        pdbEntry.setType(PDBEntry.Type.MMCIF);
+        selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
+      }
+      pdbEntriesToView[count++] = pdbEntry;
+    }
+    return pdbEntriesToView;
+  }
+
+
+  @Override
+  protected FTSRestRequest getLastFTSRequest()
+  {
+    return lastPdbRequest;
+  }
+
+
+  public FTSRestResponse executePDBFTSRestRequest(FTSRestRequest pdbRequest) throws Exception
+  {
+    return pdbRestClient.executeRequest(pdbRequest);
+  }
+
+}
\ No newline at end of file
diff --git a/src/jalview/gui/structurechooser/StructureChooserQuerySource.java b/src/jalview/gui/structurechooser/StructureChooserQuerySource.java
new file mode 100644 (file)
index 0000000..1aad2e0
--- /dev/null
@@ -0,0 +1,276 @@
+package jalview.gui.structurechooser;
+
+import java.util.Locale;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Vector;
+
+import javax.swing.JTable;
+import javax.swing.table.TableModel;
+
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.jbgui.FilterOption;
+
+/**
+ * logic for querying sources of structural data for structures of sequences
+ * 
+ * @author jprocter
+ *
+ * @param <T>
+ */
+public abstract class StructureChooserQuerySource
+{
+
+  protected FTSDataColumnPreferences docFieldPrefs;
+
+  /**
+   * max length of a GET URL (probably :( )
+   */
+  protected static int MAX_QLENGTH = 7820;
+
+  public StructureChooserQuerySource()
+  {
+  }
+
+  public static StructureChooserQuerySource getPDBfts()
+  {
+    return new PDBStructureChooserQuerySource();
+  }
+
+  public static StructureChooserQuerySource getTDBfts()
+  {
+    return new ThreeDBStructureChooserQuerySource();
+  }
+
+  public FTSDataColumnPreferences getDocFieldPrefs()
+  {
+    return docFieldPrefs;
+  }
+
+  public void setDocFieldPrefs(FTSDataColumnPreferences docFieldPrefs)
+  {
+    this.docFieldPrefs = docFieldPrefs;
+  }
+
+  public FTSDataColumnPreferences getInitialFieldPreferences()
+  {
+    return docFieldPrefs;
+  }
+
+  /**
+   * Builds a query string for a given sequences using its DBRef entries
+   * 
+   * @param seq
+   *          the sequences to build a query for
+   * @return the built query string
+   */
+
+  public abstract String buildQuery(SequenceI seq);
+
+  /**
+   * Remove the following special characters from input string +, -, &, !, (, ),
+   * {, }, [, ], ^, ", ~, *, ?, :, \
+   * 
+   * @param seqName
+   * @return
+   */
+  public static String sanitizeSeqName(String seqName)
+  {
+    Objects.requireNonNull(seqName);
+    return seqName.replaceAll("\\[\\d*\\]", "")
+            .replaceAll("[^\\dA-Za-z|_]", "").replaceAll("\\s+", "+");
+  }
+
+  /**
+   * Ensures sequence ref names are not less than 3 characters and does not
+   * contain a database name
+   * 
+   * @param seqName
+   * @return
+   */
+  static boolean isValidSeqName(String seqName)
+  {
+    // System.out.println("seqName : " + seqName);
+    String ignoreList = "pdb,uniprot,swiss-prot";
+    if (seqName.length() < 3)
+    {
+      return false;
+    }
+    if (seqName.contains(":"))
+    {
+      return false;
+    }
+    seqName = seqName.toLowerCase(Locale.ROOT);
+    for (String ignoredEntry : ignoreList.split(","))
+    {
+      if (seqName.contains(ignoredEntry))
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  static String getDBRefId(DBRefEntry dbRef)
+  {
+    String ref = dbRef.getAccessionId().replaceAll("GO:", "");
+    return ref;
+  }
+
+  static PDBEntry getFindEntry(String id, Vector<PDBEntry> pdbEntries)
+  {
+    Objects.requireNonNull(id);
+    Objects.requireNonNull(pdbEntries);
+    PDBEntry foundEntry = null;
+    for (PDBEntry entry : pdbEntries)
+    {
+      if (entry.getId().equalsIgnoreCase(id))
+      {
+        return entry;
+      }
+    }
+    return foundEntry;
+  }
+
+  /**
+   * FTSRestClient specific query builder to recover associated structure data
+   * records for a sequence
+   * 
+   * @param seq
+   *          - seq to generate a query for
+   * @param wantedFields
+   *          - fields to retrieve
+   * @param selectedFilterOpt
+   *          - criterion for ranking results (e.g. resolution)
+   * @param b
+   *          - sort ascending or descending
+   * @return
+   * @throws Exception
+   */
+  public abstract FTSRestResponse fetchStructuresMetaData(SequenceI seq,
+          Collection<FTSDataColumnI> wantedFields,
+          FilterOption selectedFilterOpt, boolean b) throws Exception;
+
+  /**
+   * FTSRestClient specific query builder to pick top ranked entry from a
+   * fetchStructuresMetaData query
+   * 
+   * @param seq
+   *          - seq to generate a query for
+   * @param discoveredStructuresSet
+   *          - existing set of entries - allows client side selection
+   * @param wantedFields
+   *          - fields to retrieve
+   * @param selectedFilterOpt
+   *          - criterion for ranking results (e.g. resolution)
+   * @param b
+   *          - sort ascending or descending
+   * @return
+   * @throws Exception
+   */
+  public abstract FTSRestResponse selectFirstRankedQuery(SequenceI seq,
+          Collection<FTSData> discoveredStructuresSet,
+          Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
+          boolean b) throws Exception;
+
+  /**
+   * 
+   * @param discoveredStructuresSet
+   * @return the table model for the given result set for this engine
+   */
+  public TableModel getTableModel(
+          Collection<FTSData> discoveredStructuresSet)
+  {
+    return FTSRestResponse.getTableModel(getLastFTSRequest(),
+            discoveredStructuresSet);
+  }
+
+  protected abstract FTSRestRequest getLastFTSRequest();
+
+  public abstract PDBEntry[] collectSelectedRows(JTable restable,
+          int[] selectedRows, List<SequenceI> selectedSeqsToView);
+
+  /**
+   * @param VIEWS_FILTER
+   *          - a String key that can be used by the caller to tag the returned
+   *          filter options to distinguish them in a collection
+   * @return list of FilterOption - convention is that the last one in the list
+   *         will be constructed with 'addSeparator==true'
+   */
+  public abstract List<FilterOption> getAvailableFilterOptions(
+          String VIEWS_FILTER);
+
+  /**
+   * construct a structure chooser query source for the given set of sequences
+   * 
+   * @param selectedSeqs
+   * @return PDBe or 3DB query source
+   */
+  public static StructureChooserQuerySource getQuerySourceFor(
+          SequenceI[] selectedSeqs)
+  {
+    ThreeDBStructureChooserQuerySource tdbSource = new ThreeDBStructureChooserQuerySource();
+    boolean hasUniprot = false, hasCanonical = false;
+    boolean hasNA = false, hasProtein = false;
+    int protWithoutUni = 0;
+    int protWithoutCanon = 0;
+    for (SequenceI seq : selectedSeqs)
+    {
+      hasNA |= !seq.isProtein();
+      hasProtein |= seq.isProtein();
+      if (seq.isProtein())
+      {
+        int refsAvailable = ThreeDBStructureChooserQuerySource
+                .checkUniprotRefs(seq.getDBRefs());
+        if (refsAvailable > -2)
+        {
+          if (refsAvailable > -1)
+          {
+            hasCanonical = true;
+          } else {
+            protWithoutCanon++;
+          }
+          hasUniprot = true;
+        } else {
+          protWithoutUni++;
+          
+        }
+      }
+    }
+    //
+    // logic: all canonicals - no fetchdb
+    // some uniprot no canonicals: defer to PDB, user can optionally fetch
+    //
+    if (hasProtein && hasCanonical && !hasNA && protWithoutCanon == 0 && protWithoutUni == 0)
+
+    {
+      return tdbSource;
+    }
+    return new PDBStructureChooserQuerySource();
+  }
+
+  /**
+   * some filter options may mean the original query needs to be executed again.
+   * 
+   * @param selectedFilterOpt
+   * @return true if the fetchStructuresMetadata method needs to be called again
+   */
+  public abstract boolean needsRefetch(FilterOption selectedFilterOpt);
+
+  public void updateAvailableFilterOptions(String VIEWS_FILTER,
+          List<FilterOption> xtantOptions, Collection<FTSData> lastFTSData)
+  {
+    // TODO Auto-generated method stub
+
+  }
+}
\ No newline at end of file
diff --git a/src/jalview/gui/structurechooser/TDBResultAnalyser.java b/src/jalview/gui/structurechooser/TDBResultAnalyser.java
new file mode 100644 (file)
index 0000000..d3896a5
--- /dev/null
@@ -0,0 +1,284 @@
+package jalview.gui.structurechooser;
+
+import java.util.Locale;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.core.FTSRestRequest;
+
+public class TDBResultAnalyser
+{
+
+  /**
+   * model categories - update as needed. warnings output if unknown types
+   * encountered.
+   * 
+   * Order denotes 'trust'
+   */
+  private static List<String> EXP_CATEGORIES = Arrays
+          .asList(new String[]
+          { "EXPERIMENTALLY DETERMINED", "DEEP-LEARNING", "TEMPLATE-BASED",
+              "AB-INITIO", "CONFORMATIONAL ENSEMBLE" });
+
+  private SequenceI seq;
+
+  private Collection<FTSData> collectedResults;
+
+  private FTSRestRequest lastTdbRequest;
+
+  private int idx_ups;
+
+  private int idx_upe;
+
+  private int idx_mcat;
+
+  private int idx_mqual;
+
+  private int idx_resol;
+
+  /**
+   * selection model
+   */
+  private String filter = null;
+
+  /**
+   * limit to particular source
+   */
+  private String sourceFilter = null;
+
+  private int idx_mprov;
+
+  public TDBResultAnalyser(SequenceI seq,
+          Collection<FTSData> collectedResults,
+          FTSRestRequest lastTdbRequest, String fieldToFilterBy,
+          String string)
+  {
+    this.seq = seq;
+    this.collectedResults = collectedResults;
+    this.lastTdbRequest = lastTdbRequest;
+    this.filter = fieldToFilterBy;
+    this.sourceFilter = string;
+    idx_ups = lastTdbRequest.getFieldIndex("Uniprot Start");
+    idx_upe = lastTdbRequest.getFieldIndex("Uniprot End");
+    idx_mcat = lastTdbRequest.getFieldIndex("Model Category");
+    idx_mprov = lastTdbRequest.getFieldIndex("Provider");
+    idx_mqual = lastTdbRequest.getFieldIndex("Confidence");
+    idx_resol = lastTdbRequest.getFieldIndex("Resolution");
+  }
+
+  /**
+   * maintain and resolve categories to 'trust order' TODO: change the trust
+   * scheme to something comprehensible.
+   * 
+   * @param cat
+   * @return 0 for null cat, less than zero for others
+   */
+  public final int scoreCategory(String cat)
+  {
+    if (cat == null)
+    {
+      return 0;
+    }
+    String upper_cat = cat.toUpperCase(Locale.ROOT);
+    int idx = EXP_CATEGORIES.indexOf(upper_cat);
+    if (idx == -1)
+    {
+      System.out.println("Unknown category: '" + cat + "'");
+      EXP_CATEGORIES.add(upper_cat);
+      idx = EXP_CATEGORIES.size() - 1;
+    }
+    return -EXP_CATEGORIES.size() - idx;
+  }
+
+  /**
+   * sorts records discovered by 3D beacons and excludes any that don't
+   * intersect with the sequence's start/end rage
+   * 
+   * @return
+   */
+  public List<FTSData> getFilteredResponse()
+  {
+    List<FTSData> filteredResponse = new ArrayList<FTSData>();
+
+    // ignore anything outside the sequence region
+    for (FTSData row : collectedResults)
+    {
+      int up_s = (Integer) row.getSummaryData()[idx_ups];
+      int up_e = (Integer) row.getSummaryData()[idx_upe];
+      String provider = (String) row.getSummaryData()[idx_mprov];
+      String mcat = (String) row.getSummaryData()[idx_mcat];
+      // this makes sure all new categories are in the score array.
+      int scorecat = scoreCategory(mcat); 
+      if (sourceFilter == null ||  sourceFilter.equals(provider))
+      {
+        if (seq == row.getSummaryData()[0] && up_e > seq.getStart()
+                && up_s < seq.getEnd())
+        {
+          filteredResponse.add(row);
+        }
+      }
+    }
+    // sort according to decreasing length,
+    // increasing start
+    Collections.sort(filteredResponse, new Comparator<FTSData>()
+    {
+      @Override
+      public int compare(FTSData o1, FTSData o2)
+      {
+        Object[] o1data = o1.getSummaryData();
+        Object[] o2data = o2.getSummaryData();
+        int o1_s = (Integer) o1data[idx_ups];
+        int o1_e = (Integer) o1data[idx_upe];
+        int o1_cat = scoreCategory((String) o1data[idx_mcat]);
+        String o1_prov= ((String) o1data[idx_mprov]).toUpperCase(Locale.ROOT);
+        int o2_s = (Integer) o2data[idx_ups];
+        int o2_e = (Integer) o2data[idx_upe];
+        int o2_cat = scoreCategory((String) o2data[idx_mcat]);
+        String o2_prov= ((String) o2data[idx_mprov]).toUpperCase(Locale.ROOT);
+        
+
+        if (o1_cat == o2_cat)
+        {
+          if (o1_s == o2_s)
+          {
+            int o1_xtent = o1_e - o1_s;
+            int o2_xtent = o2_e - o2_s;
+            if (o1_xtent == o2_xtent)
+            {
+              if (o1_cat == scoreCategory(EXP_CATEGORIES.get(0)))
+              {
+                if (o1_prov.equals(o2_prov)) {
+                  if ("PDBE".equals(o1_prov)) {
+                    if (eitherNull(idx_resol,o1data,o2data))
+                    {
+                       return nonNullFirst(idx_resol,o1data,o2data);
+                    }
+                // experimental structures, so rank on quality
+                double o1_res = (Double) o1data[idx_resol];
+                double o2_res = (Double) o2data[idx_resol];
+                return (o2_res < o1_res) ? 1 : (o2_res == o1_res) ? 0 : -1;
+                } else {
+                  return 0; // no change in order
+                }
+              } else {
+                // PDBe always ranked above all other experimentally determined categories
+                return "PDBE".equals(o1_prov) ? -1 : "PDBE".equals(o2_prov) ? 1 : 0;
+              }
+              }
+              else
+              {
+                if (eitherNull(idx_mqual,o1data, o2data)) {
+                  return nonNullFirst(idx_mqual, o1data, o2data);
+                }
+                // models, so rank on qmean - b
+                double o1_mq = (Double) o1data[idx_mqual];
+                double o2_mq = (Double) o2data[idx_mqual];
+                return (o2_mq < o1_mq) ? 1 : (o2_mq == o1_mq) ? 0 : -1;
+              }
+            }
+            else
+            {
+              return o1_xtent - o2_xtent;
+            }
+          }
+          else
+          {
+            return o1_s - o2_s;
+          }
+        }
+        else
+        {
+          return o2_cat - o1_cat;
+        }
+      }
+
+      private int nonNullFirst(int idx_resol, Object[] o1data,
+              Object[] o2data)
+      {
+          return o1data[idx_resol] == o2data[idx_resol] ? 0: o1data[idx_resol] != null ? -1 : 1; 
+      }
+
+      private boolean eitherNull(int idx_resol, Object[] o1data,
+              Object[] o2data)
+      {
+        return (o1data[idx_resol] == null || o2data[idx_resol]==null);
+      }
+
+      @Override
+      public boolean equals(Object obj)
+      {
+        return super.equals(obj);
+      }
+    });
+    return filteredResponse;
+  }
+
+  /**
+   * return list of structures to be marked as selected for this sequence
+   * according to given criteria
+   * 
+   * @param filteredStructures
+   *          - sorted, filtered structures from getFilteredResponse
+   * 
+   */
+  public List<FTSData> selectStructures(List<FTSData> filteredStructures)
+  {
+    List<FTSData> selected = new ArrayList<FTSData>();
+    BitSet cover = new BitSet();
+    cover.set(seq.getStart(), seq.getEnd());
+    // walk down the list of structures, selecting some to add to selected
+    for (FTSData structure : filteredStructures)
+    {
+      Object[] odata = structure.getSummaryData();
+      int o1_s = (Integer) odata[idx_ups];
+      int o1_e = (Integer) odata[idx_upe];
+      int o1_cat = scoreCategory((String) odata[idx_mcat]);
+      BitSet scover = new BitSet();
+      // measure intersection
+      scover.set(o1_s, o1_e);
+      scover.and(cover);
+      if (scover.cardinality() > 4)
+      {
+        selected.add(structure);
+        // clear the range covered by this structure
+        cover.andNot(scover);
+      }
+    }
+    if (selected.size()==0)
+    {
+      return selected;
+    }
+    // final step is to sort on length - this might help the superposition
+    // process
+    Collections.sort(selected, new Comparator<FTSData>()
+    {
+      @Override
+      public int compare(FTSData o1, FTSData o2)
+      {
+        Object[] o1data = o1.getSummaryData();
+        Object[] o2data = o2.getSummaryData();
+        int o1_xt = ((Integer) o1data[idx_upe])
+                - ((Integer) o1data[idx_ups]);
+        int o1_cat = scoreCategory((String) o1data[idx_mcat]);
+        int o2_xt = ((Integer) o2data[idx_upe] - (Integer) o2data[idx_ups]);
+        int o2_cat = scoreCategory((String) o2data[idx_mcat]);
+        return o2_xt - o1_xt;
+      }
+    });
+    if (filter.equals(
+            ThreeDBStructureChooserQuerySource.FILTER_FIRST_BEST_COVERAGE))
+    {
+      return selected.subList(0, 1);
+    }
+    return selected;
+  }
+
+}
diff --git a/src/jalview/gui/structurechooser/ThreeDBStructureChooserQuerySource.java b/src/jalview/gui/structurechooser/ThreeDBStructureChooserQuerySource.java
new file mode 100644 (file)
index 0000000..cdf456c
--- /dev/null
@@ -0,0 +1,569 @@
+package jalview.gui.structurechooser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.swing.JTable;
+
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSRestClientI;
+import jalview.fts.core.FTSDataColumnPreferences;
+import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.service.threedbeacons.TDB_FTSData;
+import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient;
+import jalview.jbgui.FilterOption;
+
+/**
+ * logic for querying the 3DBeacons API for structures of sequences
+ * 
+ * @author jprocter
+ */
+public class ThreeDBStructureChooserQuerySource
+        extends StructureChooserQuerySource
+{
+
+  private Set<String> tdBeaconsFilters = null, defaultFilters = null;
+
+  public static final String FILTER_TDBEACONS_COVERAGE = "3d_beacons_coverage";
+
+  public static final String FILTER_FIRST_BEST_COVERAGE = "3d_beacons_first_best_coverage";
+
+  private static final String FILTER_SOURCE_PREFIX = "only_";
+
+  private static int MAX_QLENGTH = 7820;
+
+  protected FTSRestRequest lastTdbRequest;
+
+  protected FTSRestClientI tdbRestClient;
+
+  private FTSRestRequest lastPdbRequest;
+
+  public ThreeDBStructureChooserQuerySource()
+  {
+    defaultFilters = new LinkedHashSet<String>();
+    defaultFilters.add(FILTER_TDBEACONS_COVERAGE);
+    defaultFilters.add(FILTER_FIRST_BEST_COVERAGE);
+
+    tdbRestClient = TDBeaconsFTSRestClient.getInstance();
+    docFieldPrefs = new FTSDataColumnPreferences(
+            PreferenceSource.STRUCTURE_CHOOSER,
+            TDBeaconsFTSRestClient.getInstance());
+  }
+
+  /**
+   * Builds a query string for a given sequences using its DBRef entries 3d
+   * Beacons is only useful for uniprot IDs
+   * 
+   * @param seq
+   *          the sequences to build a query for
+   * @return the built query string
+   */
+
+  public String buildQuery(SequenceI seq)
+  {
+    boolean isPDBRefsFound = false;
+    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<>();
+
+    List<DBRefEntry> refs = seq.getDBRefs();
+    int ib = checkUniprotRefs(refs);
+    if (ib>-1)
+    {
+      return getDBRefId(refs.get(ib));
+    }
+    return null;
+  }
+
+  /**
+   * Searches DBRefEntry for uniprot refs
+   * @param seq
+   * @return -2 if no uniprot refs, -1 if no canonical ref., otherwise index of Uniprot canonical DBRefEntry 
+   */
+  public static int checkUniprotRefs(List<DBRefEntry> refs)
+  {
+    boolean hasUniprot = false;
+    if (refs != null && refs.size() != 0)
+    {
+      for (int ib = 0, nb = refs.size(); ib < nb; ib++)
+      {
+        DBRefEntry dbRef = refs.get(ib);
+        if (dbRef.getSource().equalsIgnoreCase(DBRefSource.UNIPROT))
+        {
+          hasUniprot = true;
+          if (dbRef.isCanonical())
+          {
+            return ib;
+          }
+        }
+      }
+    }
+    return hasUniprot ? -1 : -2;
+  }
+
+  /**
+   * Ensures sequence ref names are not less than 3 characters and does not
+   * contain a database name
+   * 
+   * @param seqName
+   * @return
+   */
+  static boolean isValidSeqName(String seqName)
+  {
+    // System.out.println("seqName : " + seqName);
+    String ignoreList = "pdb,uniprot,swiss-prot";
+    if (seqName.length() < 3)
+    {
+      return false;
+    }
+    if (seqName.contains(":"))
+    {
+      return false;
+    }
+    seqName = seqName.toLowerCase(Locale.ROOT);
+    for (String ignoredEntry : ignoreList.split(","))
+    {
+      if (seqName.contains(ignoredEntry))
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  static String getDBRefId(DBRefEntry dbRef)
+  {
+    String ref = dbRef.getAccessionId().replaceAll("GO:", "");
+    return ref;
+  }
+
+  /**
+   * FTSRestClient specific query builder to recover associated structure data
+   * records for a sequence
+   * 
+   * @param seq
+   *          - seq to generate a query for
+   * @param wantedFields
+   *          - fields to retrieve
+   * @param selectedFilterOpt
+   *          - criterion for ranking results (e.g. resolution)
+   * @param b
+   *          - sort ascending or descending
+   * @return
+   * @throws Exception
+   */
+  public FTSRestResponse fetchStructuresMetaData(SequenceI seq,
+          Collection<FTSDataColumnI> wantedFields,
+          FilterOption selectedFilterOpt, boolean b) throws Exception
+  {
+    FTSRestResponse resultList;
+    if (selectedFilterOpt != null
+            && tdBeaconsFilter(selectedFilterOpt.getValue()))
+    {
+      FTSRestRequest tdbRequest = getTDBeaconsRequest(seq, wantedFields);
+      resultList = tdbRestClient.executeRequest(tdbRequest);
+      
+      lastTdbRequest = tdbRequest;
+      if (resultList!=null)
+      { // Query the PDB and add additional metadata
+        FTSRestResponse pdbResponse = fetchStructuresMetaDataFor(
+                getPDBQuerySource(), resultList);
+        FTSRestResponse joinedResp = joinResponses(resultList, pdbResponse);
+      }
+      return resultList;
+    }
+    // use the PDBFTS directly
+    resultList = getPDBQuerySource().fetchStructuresMetaData(seq,
+            wantedFields, selectedFilterOpt, b);
+    lastTdbRequest = getPDBQuerySource().lastPdbRequest;
+    lastPdbRequest = lastTdbRequest; // both queries the same - indicates we
+    // rank using PDBe
+    return resultList;
+
+  }
+
+  PDBStructureChooserQuerySource pdbQuerySource = null;
+
+  private PDBStructureChooserQuerySource getPDBQuerySource()
+  {
+    if (pdbQuerySource == null)
+    {
+      pdbQuerySource = new PDBStructureChooserQuerySource();
+    }
+    return pdbQuerySource;
+  }
+
+  private FTSRestRequest getTDBeaconsRequest(SequenceI seq,
+          Collection<FTSDataColumnI> wantedFields)
+  {
+    FTSRestRequest pdbRequest = new FTSRestRequest();
+    pdbRequest.setAllowEmptySeq(false);
+    pdbRequest.setResponseSize(500);
+    pdbRequest.setWantedFields(wantedFields);
+    String query = buildQuery(seq);
+    if (query == null)
+    {
+      return null;
+    }
+    pdbRequest.setSearchTerm(query + ".json");
+    pdbRequest.setAssociatedSequence(seq);
+    return pdbRequest;
+  }
+
+  @Override
+  public List<FilterOption> getAvailableFilterOptions(String VIEWS_FILTER)
+  {
+    List<FilterOption> filters = getPDBQuerySource()
+            .getAvailableFilterOptions(VIEWS_FILTER);
+    tdBeaconsFilters = new LinkedHashSet<String>();
+    tdBeaconsFilters.addAll(defaultFilters);
+    filters.add(0, new FilterOption("Best 3D-Beacons Coverage",
+            FILTER_FIRST_BEST_COVERAGE, VIEWS_FILTER, false, this));
+    filters.add(1, new FilterOption("Multiple 3D-Beacons Coverage",
+            FILTER_TDBEACONS_COVERAGE, VIEWS_FILTER, true, this));
+
+    return filters;
+  }
+
+  @Override
+  public void updateAvailableFilterOptions(String VIEWS_FILTER,
+          List<FilterOption> xtantOptions, Collection<FTSData> tdbEntries)
+  {
+    if (tdbEntries != null && lastTdbRequest != null)
+    {
+      int prov_idx = lastTdbRequest.getFieldIndex("Provider");
+      boolean hasPDBe=false;
+      for (FTSData _row : tdbEntries)
+      {
+        // tdb returns custom object
+        TDB_FTSData row = (TDB_FTSData) _row;
+        String provider = (String) row.getProvider();
+        FilterOption providerOpt = new FilterOption(
+                "3DB Provider - " + provider,
+                FILTER_SOURCE_PREFIX + provider, VIEWS_FILTER, false, this);
+        if (!xtantOptions.contains(providerOpt))
+        {
+          xtantOptions.add(1, providerOpt);
+          tdBeaconsFilters.add(FILTER_SOURCE_PREFIX + provider);
+          if ("PDBe".equalsIgnoreCase(provider))
+          {
+            hasPDBe=true;
+          }
+        }
+      }
+      if (!hasPDBe)
+      {
+        // remove the PDBe options from the available filters
+        int op=0;
+        while (op<xtantOptions.size())
+        {
+          FilterOption filter = xtantOptions.get(op);
+          if (filter.getQuerySource() instanceof PDBStructureChooserQuerySource)
+          {
+            xtantOptions.remove(op);
+          } else {
+            op++;
+          }
+        }
+      }
+    }
+
+  }
+
+  private boolean tdBeaconsFilter(String fieldToFilterBy)
+  {
+    return tdBeaconsFilters != null
+            && tdBeaconsFilters.contains(fieldToFilterBy);
+  }
+
+  private String remove_prefix(String fieldToFilterBy)
+  {
+    if (tdBeaconsFilters != null
+            && tdBeaconsFilters.contains(fieldToFilterBy)
+            && !defaultFilters.contains(fieldToFilterBy))
+    {
+      return fieldToFilterBy.substring(FILTER_SOURCE_PREFIX.length());
+    }
+    else
+    {
+      return null;
+    }
+  }
+
+  @Override
+  public boolean needsRefetch(FilterOption selectedFilterOpt)
+  {
+    return selectedFilterOpt == null
+            || !tdBeaconsFilter(selectedFilterOpt.getValue())
+                    && lastPdbRequest != lastTdbRequest;
+  }
+
+  /**
+   * FTSRestClient specific query builder to pick top ranked entry from a
+   * fetchStructuresMetaData query
+   * 
+   * @param seq
+   *          - seq to generate a query for
+   * @param wantedFields
+   *          - fields to retrieve
+   * @param selectedFilterOpt
+   *          - criterion for ranking results (e.g. resolution)
+   * @param b
+   *          - sort ascending or descending
+   * @return
+   * @throws Exception
+   */
+  public FTSRestResponse selectFirstRankedQuery(SequenceI seq,
+          Collection<FTSData> collectedResults,
+          Collection<FTSDataColumnI> wantedFields, String fieldToFilterBy,
+          boolean b) throws Exception
+  {
+    if (fieldToFilterBy != null && tdBeaconsFilter(fieldToFilterBy))
+    {
+      TDBResultAnalyser analyser = new TDBResultAnalyser(seq,
+              collectedResults, lastTdbRequest, fieldToFilterBy,
+              remove_prefix(fieldToFilterBy));
+
+      FTSRestResponse resultList = new FTSRestResponse();
+
+      List<FTSData> filteredResponse = analyser.getFilteredResponse();
+
+      List<FTSData> selectedStructures = analyser
+              .selectStructures(filteredResponse);
+      resultList.setNumberOfItemsFound(selectedStructures.size());
+      resultList.setSearchSummary(selectedStructures);
+      return resultList;
+    }
+    // Fall back to PDBe rankings
+    return getPDBQuerySource().selectFirstRankedQuery(seq, collectedResults,
+            wantedFields, fieldToFilterBy, b);
+  }
+
+  @Override
+  public PDBEntry[] collectSelectedRows(JTable restable, int[] selectedRows,
+          List<SequenceI> selectedSeqsToView)
+  {
+    int refSeqColIndex = restable.getColumn("Ref Sequence").getModelIndex();
+
+    PDBEntry[] pdbEntriesToView = new PDBEntry[selectedRows.length];
+    int count = 0;
+    int idColumnIndex = restable.getColumn("Model id").getModelIndex();
+    int urlColumnIndex = restable.getColumn("Url").getModelIndex();
+    int typeColumnIndex = restable.getColumn("Provider").getModelIndex();
+    int humanUrl = restable.getColumn("Page URL").getModelIndex();
+    int categoryColumnIndex = restable.getColumn("Model Category")
+            .getModelIndex();
+    final int up_start_idx = restable.getColumn("Uniprot Start")
+            .getModelIndex();
+    final int up_end_idx = restable.getColumn("Uniprot End")
+            .getModelIndex();
+    int i = 0;
+
+    // bleugh!
+    Integer[] sellist = new Integer[selectedRows.length];
+    for (Integer row : selectedRows)
+    {
+      sellist[i++] = row;
+    }
+    // Sort rows by coverage
+    Arrays.sort(sellist, new Comparator<Integer>()
+    {
+      @Override
+      public int compare(Integer o1, Integer o2)
+      {
+        int o1_xt = ((Integer) restable.getValueAt(o1, up_end_idx))
+                - (Integer) restable.getValueAt(o1, up_start_idx);
+        int o2_xt = ((Integer) restable.getValueAt(o2, up_end_idx))
+                - (Integer) restable.getValueAt(o2, up_start_idx);
+        return o2_xt - o1_xt;
+      }
+    });
+
+    for (int row : sellist)
+    {
+      // unique id - could be a horrible hash
+
+      String pdbIdStr = restable.getValueAt(row, idColumnIndex).toString();
+      String urlStr = restable.getValueAt(row, urlColumnIndex).toString();
+      String typeColumn = restable.getValueAt(row, typeColumnIndex)
+              .toString();
+      String modelPage = humanUrl < 1 ? null
+              : (String) restable.getValueAt(row, humanUrl);
+      SequenceI selectedSeq = (SequenceI) restable.getValueAt(row,
+              refSeqColIndex);
+      selectedSeqsToView.add(selectedSeq);
+      PDBEntry pdbEntry = selectedSeq.getPDBEntry(pdbIdStr);
+      if (pdbEntry == null)
+      {
+        pdbEntry = getFindEntry(pdbIdStr, selectedSeq.getAllPDBEntries());
+      }
+
+      if (pdbEntry == null)
+      {
+        pdbEntry = new PDBEntry();
+        pdbEntry.setId(pdbIdStr);
+        boolean hasCif = urlStr.toLowerCase(Locale.ENGLISH).endsWith("cif");
+        boolean probablyPdb = urlStr.toLowerCase(Locale.ENGLISH)
+                .contains("pdb");
+        pdbEntry.setType(hasCif ? PDBEntry.Type.MMCIF
+                : probablyPdb ? PDBEntry.Type.PDB : PDBEntry.Type.FILE);
+        if (!"PDBe".equalsIgnoreCase(typeColumn))
+        {
+          pdbEntry.setRetrievalUrl(urlStr);
+        }
+        pdbEntry.setProvider(typeColumn);
+        pdbEntry.setProviderPage(modelPage);
+        selectedSeq.getDatasetSequence().addPDBId(pdbEntry);
+      }
+      pdbEntriesToView[count++] = pdbEntry;
+    }
+    return pdbEntriesToView;
+  }
+
+  @Override
+  protected FTSRestRequest getLastFTSRequest()
+  {
+    return lastTdbRequest;
+  }
+
+  /**
+   * generate a query for PDBFTS to retrieve structure metadata
+   * 
+   * @param ftsRestRequest
+   * @param upResponse
+   * @return
+   */
+
+  public String buildPDBFTSQueryFor(FTSRestResponse upResponse)
+  {
+    List<String> pdbIds = new ArrayList<String>();
+    int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
+    int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
+    for (FTSData row : upResponse.getSearchSummary())
+    {
+      String id = (String) row.getSummaryData()[idx_modelId];
+      String provider = (String) row.getSummaryData()[idx_provider];
+      if ("PDBe".equalsIgnoreCase(provider))
+      {
+        pdbIds.add(id);
+      }
+    }
+    return String.join(" OR ", pdbIds).toString();
+  }
+
+  /**
+   * query PDBe for structure metadata
+   * 
+   * @param pdbquery
+   * @param upResponse
+   * @return FTSRestResponse via PDBStructureChooserQuerySource
+   */
+  public FTSRestResponse fetchStructuresMetaDataFor(
+          PDBStructureChooserQuerySource pdbquery,
+          FTSRestResponse upResponse) throws Exception
+  {
+
+    String pdb_Query = buildPDBFTSQueryFor(upResponse);
+    if (pdb_Query.length() == 0)
+    {
+      return null;
+    }
+    FTSRestResponse resultList;
+    FTSRestRequest pdbRequest = new FTSRestRequest();
+    pdbRequest.setAllowEmptySeq(false);
+    pdbRequest.setResponseSize(500);
+    pdbRequest.setFieldToSearchBy("(");
+    // pdbRequest.setFieldToSortBy("pdb_id");
+    pdbRequest.setWantedFields(
+            pdbquery.getDocFieldPrefs().getStructureSummaryFields());
+    pdbRequest.setSearchTerm(pdb_Query + ")");
+
+    resultList = pdbquery.executePDBFTSRestRequest(pdbRequest);
+
+    lastPdbRequest = pdbRequest;
+    return resultList;
+  }
+
+  public FTSRestResponse joinResponses(FTSRestResponse upResponse,
+          FTSRestResponse pdbResponse)
+  {
+    boolean hasPdbResp = lastPdbRequest != null;
+
+    int idx_provider = getLastFTSRequest().getFieldIndex("Provider");
+    // join on
+    int idx_modelId = getLastFTSRequest().getFieldIndex("Model id");
+    int pdbIdx = hasPdbResp ? lastPdbRequest.getFieldIndex("PDB Id") : -1;
+    int pdbTitle_idx = hasPdbResp ? lastPdbRequest.getFieldIndex("Title")
+            : -1;
+    int tdbTitle_idx = getLastFTSRequest().getFieldIndex("Title");
+
+    List<FTSData> joinedRows = new ArrayList<FTSData>();
+    for (final FTSData row : upResponse.getSearchSummary())
+    {
+      String id = (String) row.getSummaryData()[idx_modelId];
+      String provider = (String) row.getSummaryData()[idx_provider];
+      if ("PDBe".equalsIgnoreCase(provider))
+      {
+        if (!hasPdbResp)
+        {
+          System.out.println(
+                  "Warning: seems like we couldn't get to the PDBe search interface.");
+        }
+        else
+        {
+          for (final FTSData pdbrow : pdbResponse.getSearchSummary())
+          {
+            String pdbid = (String) pdbrow.getSummaryData()[pdbIdx];
+            if (id.equalsIgnoreCase(pdbid))
+            {
+              row.getSummaryData()[tdbTitle_idx] = pdbrow
+                      .getSummaryData()[pdbTitle_idx];
+            }
+          }
+        }
+
+      }
+      else
+      {
+        row.getSummaryData()[tdbTitle_idx] = "Model from TDB";
+      }
+    }
+    return upResponse;
+  }
+
+  public TDB_FTSData getFTSDataFor(JTable restable, int selectedRow,
+          Collection<FTSData> discoveredStructuresSet)
+  {
+    int idColumnIndex = restable.getColumn("Model id").getModelIndex();
+    
+    String modelId = (String) restable.getValueAt(selectedRow, idColumnIndex);
+    for (FTSData row:discoveredStructuresSet)
+    {
+      if (row instanceof TDB_FTSData && ((TDB_FTSData)row).getModelId().equals(modelId))
+      {
+        return ((TDB_FTSData)row);
+      }
+    }
+    return null;
+  }
+
+}
\ No newline at end of file
index 2182b24..934be41 100755 (executable)
@@ -25,6 +25,7 @@ import java.io.BufferedReader;
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
@@ -1086,8 +1087,8 @@ public class AnnotationFile
               if (rowset != null && rowset.size() > 0)
               {
                 AlignmentAnnotation alan = null;
-                for (int elm = 0, elmSize = rowset
-                        .size(); elm < elmSize; elm++)
+                for (int elm = 0,
+                        elmSize = rowset.size(); elm < elmSize; elm++)
                 {
                   alan = (AlignmentAnnotation) rowset.elementAt(elm);
                   alan.groupRef = theGroup;
@@ -1549,110 +1550,120 @@ public class AnnotationFile
     }
 
     String name = st.nextToken();
-    SequenceGroup sg = null;
-    for (SequenceGroup _sg : al.getGroups())
+
+    Map<String, String> properties = new HashMap<>();
+    while (st.hasMoreTokens())
     {
-      if ((sg = _sg).getName().equals(name))
-      {
-        break;
-      }
-      else
+      String keyValue = st.nextToken();
+      String key = keyValue.substring(0, keyValue.indexOf("="));
+      String value = keyValue.substring(keyValue.indexOf("=") + 1);
+      properties.put(key, value);
+    }
+
+    for (SequenceGroup sg : al.getGroups())
+    {
+      if (sg.getName().equals(name))
       {
-        sg = null;
+        addProperties(sg, properties, al);
       }
     }
+  }
 
-    if (sg != null)
+  /**
+   * Helper method that applies any specified properties to a SequenceGroup
+   * 
+   * @param sg
+   * @param properties
+   * @param al
+   */
+  private void addProperties(SequenceGroup sg,
+          Map<String, String> properties, AlignmentI al)
+  {
+    ColourSchemeI def = sg.getColourScheme();
+    for (String key : properties.keySet())
     {
-      String keyValue, key, value;
-      ColourSchemeI def = sg.getColourScheme();
-      while (st.hasMoreTokens())
+      String value = properties.get(key);
+      if (key.equalsIgnoreCase("description"))
       {
-        keyValue = st.nextToken();
-        key = keyValue.substring(0, keyValue.indexOf("="));
-        value = keyValue.substring(keyValue.indexOf("=") + 1);
-
-        if (key.equalsIgnoreCase("description"))
-        {
-          sg.setDescription(value);
-        }
-        else if (key.equalsIgnoreCase("colour"))
-        {
-          // TODO need to notify colourscheme of view reference once it is
-          // available
-          sg.cs.setColourScheme(
-                  ColourSchemeProperty.getColourScheme(null, al, value));
-        }
-        else if (key.equalsIgnoreCase("pidThreshold"))
-        {
-          sg.cs.setThreshold(Integer.parseInt(value), true);
+        sg.setDescription(value);
+      }
+      else if (key.equalsIgnoreCase("colour"))
+      {
+        // TODO need to notify colourscheme of view reference once it is
+        // available
+        sg.cs.setColourScheme(
+                ColourSchemeProperty.getColourScheme(null, al, value));
+      }
+      else if (key.equalsIgnoreCase("pidThreshold"))
+      {
+        sg.cs.setThreshold(Integer.parseInt(value), true);
 
-        }
-        else if (key.equalsIgnoreCase("consThreshold"))
-        {
-          sg.cs.setConservationInc(Integer.parseInt(value));
-          Conservation c = new Conservation("Group", sg.getSequences(null),
-                  sg.getStartRes(), sg.getEndRes() + 1);
+      }
+      else if (key.equalsIgnoreCase("consThreshold"))
+      {
+        sg.cs.setConservationInc(Integer.parseInt(value));
+        Conservation c = new Conservation("Group", sg.getSequences(null),
+                sg.getStartRes(), sg.getEndRes() + 1);
 
-          c.calculate();
-          c.verdict(false, 25); // TODO: refer to conservation percent threshold
+        c.calculate();
+        c.verdict(false, 25); // TODO: refer to conservation percent threshold
 
-          sg.cs.setConservation(c);
+        sg.cs.setConservation(c);
 
-        }
-        else if (key.equalsIgnoreCase("outlineColour"))
-        {
-          sg.setOutlineColour(ColorUtils.parseColourString(value));
-        }
-        else if (key.equalsIgnoreCase("displayBoxes"))
-        {
-          sg.setDisplayBoxes(Boolean.valueOf(value).booleanValue());
-        }
-        else if (key.equalsIgnoreCase("showUnconserved"))
-        {
-          sg.setShowNonconserved(Boolean.valueOf(value).booleanValue());
-        }
-        else if (key.equalsIgnoreCase("displayText"))
-        {
-          sg.setDisplayText(Boolean.valueOf(value).booleanValue());
-        }
-        else if (key.equalsIgnoreCase("colourText"))
-        {
-          sg.setColourText(Boolean.valueOf(value).booleanValue());
-        }
-        else if (key.equalsIgnoreCase("textCol1"))
-        {
-          sg.textColour = ColorUtils.parseColourString(value);
-        }
-        else if (key.equalsIgnoreCase("textCol2"))
-        {
-          sg.textColour2 = ColorUtils.parseColourString(value);
-        }
-        else if (key.equalsIgnoreCase("textColThreshold"))
-        {
-          sg.thresholdTextColour = Integer.parseInt(value);
-        }
-        else if (key.equalsIgnoreCase("idColour"))
-        {
-          Color idColour = ColorUtils.parseColourString(value);
-          sg.setIdColour(idColour == null ? Color.black : idColour);
-        }
-        else if (key.equalsIgnoreCase("hide"))
-        {
-          // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847
-          sg.setHidereps(true);
-        }
-        else if (key.equalsIgnoreCase("hidecols"))
-        {
-          // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847
-          sg.setHideCols(true);
-        }
-        sg.recalcConservation();
       }
-      if (sg.getColourScheme() == null)
+      else if (key.equalsIgnoreCase("outlineColour"))
       {
-        sg.setColourScheme(def);
+        sg.setOutlineColour(ColorUtils.parseColourString(value));
       }
+      else if (key.equalsIgnoreCase("displayBoxes"))
+      {
+        sg.setDisplayBoxes(Boolean.valueOf(value).booleanValue());
+      }
+      else if (key.equalsIgnoreCase("showUnconserved"))
+      {
+        sg.setShowNonconserved(Boolean.valueOf(value).booleanValue());
+      }
+      else if (key.equalsIgnoreCase("displayText"))
+      {
+        sg.setDisplayText(Boolean.valueOf(value).booleanValue());
+      }
+      else if (key.equalsIgnoreCase("colourText"))
+      {
+        sg.setColourText(Boolean.valueOf(value).booleanValue());
+      }
+      else if (key.equalsIgnoreCase("textCol1"))
+      {
+        sg.textColour = ColorUtils.parseColourString(value);
+      }
+      else if (key.equalsIgnoreCase("textCol2"))
+      {
+        sg.textColour2 = ColorUtils.parseColourString(value);
+      }
+      else if (key.equalsIgnoreCase("textColThreshold"))
+      {
+        sg.thresholdTextColour = Integer.parseInt(value);
+      }
+      else if (key.equalsIgnoreCase("idColour"))
+      {
+        Color idColour = ColorUtils.parseColourString(value);
+        sg.setIdColour(idColour == null ? Color.black : idColour);
+      }
+      else if (key.equalsIgnoreCase("hide"))
+      {
+        // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847
+        sg.setHidereps(true);
+      }
+      else if (key.equalsIgnoreCase("hidecols"))
+      {
+        // see bug https://mantis.lifesci.dundee.ac.uk/view.php?id=25847
+        sg.setHideCols(true);
+      }
+      sg.recalcConservation();
+    }
+
+    if (sg.getColourScheme() == null)
+    {
+      sg.setColourScheme(def);
     }
   }
 
index 4916bb3..e9c9ce2 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import jalview.api.AlignExportSettingsI;
 import jalview.api.AlignmentViewPanel;
 import jalview.datamodel.Alignment;
@@ -443,7 +445,7 @@ public class AppletFormatAdapter
     
     String data = dataObject.toString();
     DataSourceType protocol = DataSourceType.PASTE;
-    String ft = data.toLowerCase().trim();
+    String ft = data.toLowerCase(Locale.ROOT).trim();
     if (ft.indexOf("http:") == 0 || ft.indexOf("https:") == 0
             || ft.indexOf("file:") == 0)
     {
diff --git a/src/jalview/io/EMBLLikeFlatFile.java b/src/jalview/io/EMBLLikeFlatFile.java
new file mode 100644 (file)
index 0000000..64943b2
--- /dev/null
@@ -0,0 +1,833 @@
+package jalview.io;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import jalview.bin.Cache;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.FeatureProperties;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.util.DBRefUtils;
+import jalview.util.DnaUtils;
+import jalview.util.MapList;
+import jalview.util.MappingUtils;
+
+/**
+ * A base class to support parsing of GenBank, EMBL or DDBJ flat file format
+ * data. Example files (rather than formal specifications) are provided at
+ * 
+ * <pre>
+ * https://ena-docs.readthedocs.io/en/latest/submit/fileprep/flat-file-example.html
+ * https://www.ncbi.nlm.nih.gov/Sitemap/samplerecord.html
+ * </pre>
+ * 
+ * or to compare the same entry, see
+ * 
+ * <pre>
+ * https://www.ebi.ac.uk/ena/browser/api/embl/X81322.1
+ * https://www.ncbi.nlm.nih.gov/nuccore/X81322.1
+ * </pre>
+ * 
+ * The feature table part of the file has a common definition, only the start of
+ * each line is formatted differently in GenBank and EMBL. See
+ * http://www.insdc.org/files/feature_table.html#7.1.
+ */
+public abstract class EMBLLikeFlatFile extends AlignFile
+{
+  protected static final String LOCATION = "location";
+
+  protected static final String QUOTE = "\"";
+
+  protected static final String DOUBLED_QUOTE = QUOTE + QUOTE;
+
+  protected static final String WHITESPACE = "\\s+";
+
+  /**
+   * Removes leading or trailing double quotes (") unless doubled, and changes
+   * any 'escaped' (doubled) double quotes to single characters. As per the
+   * Feature Table specification for Qualifiers, Free Text.
+   * 
+   * @param value
+   * @return
+   */
+  protected static String removeQuotes(String value)
+  {
+    if (value == null)
+    {
+      return null;
+    }
+    if (value.startsWith(QUOTE) && !value.startsWith(DOUBLED_QUOTE))
+    {
+      value = value.substring(1);
+    }
+    if (value.endsWith(QUOTE) && !value.endsWith(DOUBLED_QUOTE))
+    {
+      value = value.substring(0, value.length() - 1);
+    }
+    value = value.replace(DOUBLED_QUOTE, QUOTE);
+    return value;
+  }
+
+  /**
+   * Truncates (if necessary) the exon intervals to match 3 times the length of
+   * the protein; also accepts 3 bases longer (for stop codon not included in
+   * protein)
+   * 
+   * @param proteinLength
+   * @param exon
+   *          an array of [start, end, start, end...] intervals
+   * @return the same array (if unchanged) or a truncated copy
+   */
+  protected static int[] adjustForProteinLength(int proteinLength,
+          int[] exon)
+  {
+    if (proteinLength <= 0 || exon == null)
+    {
+      return exon;
+    }
+    int expectedCdsLength = proteinLength * 3;
+    int exonLength = MappingUtils.getLength(Arrays.asList(exon));
+
+    /*
+     * if exon length matches protein, or is shorter, or longer by the 
+     * length of a stop codon (3 bases), then leave it unchanged
+     */
+    if (expectedCdsLength >= exonLength
+            || expectedCdsLength == exonLength - 3)
+    {
+      return exon;
+    }
+
+    int origxon[];
+    int sxpos = -1;
+    int endxon = 0;
+    origxon = new int[exon.length];
+    System.arraycopy(exon, 0, origxon, 0, exon.length);
+    int cdspos = 0;
+    for (int x = 0; x < exon.length; x += 2)
+    {
+      cdspos += Math.abs(exon[x + 1] - exon[x]) + 1;
+      if (expectedCdsLength <= cdspos)
+      {
+        // advanced beyond last codon.
+        sxpos = x;
+        if (expectedCdsLength != cdspos)
+        {
+          // System.err
+          // .println("Truncating final exon interval on region by "
+          // + (cdspos - cdslength));
+        }
+
+        /*
+         * shrink the final exon - reduce end position if forward
+         * strand, increase it if reverse
+         */
+        if (exon[x + 1] >= exon[x])
+        {
+          endxon = exon[x + 1] - cdspos + expectedCdsLength;
+        }
+        else
+        {
+          endxon = exon[x + 1] + cdspos - expectedCdsLength;
+        }
+        break;
+      }
+    }
+
+    if (sxpos != -1)
+    {
+      // and trim the exon interval set if necessary
+      int[] nxon = new int[sxpos + 2];
+      System.arraycopy(exon, 0, nxon, 0, sxpos + 2);
+      nxon[sxpos + 1] = endxon; // update the end boundary for the new exon
+                                // set
+      exon = nxon;
+    }
+    return exon;
+  }
+
+  /*
+   * when true, interpret the mol_type 'source' feature attribute
+   * and generate an RNA sequence from the DNA record
+   */
+  protected boolean produceRna=true;
+    
+
+  /*
+   * values parsed from the data file
+   */
+  protected String sourceDb;
+
+  protected String accession;
+
+  protected String version;
+
+  protected String description;
+
+  protected int length = 128;
+
+  protected List<DBRefEntry> dbrefs;
+
+  protected boolean sequenceStringIsRNA=false;
+
+  protected String sequenceString;
+
+  protected Map<String, CdsData> cds;
+
+  /**
+   * Constructor
+   * 
+   * @param fp
+   * @param sourceId
+   * @throws IOException
+   */
+  public EMBLLikeFlatFile(FileParse fp, String sourceId) throws IOException
+  {
+    super(false, fp); // don't parse immediately
+    this.sourceDb = sourceId;
+    dbrefs = new ArrayList<>();
+
+    /*
+     * using TreeMap gives CDS sequences in alphabetical, so readable, order
+     */
+    cds = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    
+    parse();
+  }
+
+  /**
+   * process attributes for 'source' until the next FT feature entry
+   * only interested in 'mol_type'
+   * @param tokens
+   * @return
+   * @throws IOException
+   */
+  private String parseSourceQualifiers(String[] tokens) throws IOException
+  {
+    if (!"source".equals(tokens[0]))
+    {
+      throw (new RuntimeException("Not given a 'source' qualifier line"));
+    }
+    // search for mol_type attribute
+
+    StringBuilder sb = new StringBuilder().append(tokens[1]); // extent of
+                                                              // sequence
+
+    String line = parseFeatureQualifier(sb, false);
+    while (line != null)
+    {
+      if (!line.startsWith("FT    ")) // four spaces, end of this feature table
+                                      // entry
+      {
+        return line;
+      }
+
+      // case sensitive ?
+      int p = line.indexOf("\\mol_type");
+      int qs = line.indexOf("\"", p);
+      int qe = line.indexOf("\"", qs + 1);
+      String qualifier=line.substring(qs,qe).toLowerCase(Locale.ROOT);
+      if (qualifier.indexOf("rna") > -1)
+      {
+        sequenceStringIsRNA = true;
+      }
+      if (qualifier.indexOf("dna") > -1)
+      {
+        sequenceStringIsRNA = false;
+      }
+      line=parseFeatureQualifier(sb, false);
+    }
+    return line;
+  }
+
+   
+  /**
+   * Parses one (GenBank or EMBL format) CDS feature, saves the parsed data, and
+   * returns the next line
+   * 
+   * @param location
+   * @return
+   * @throws IOException
+   */
+  protected String parseCDSFeature(String location) throws IOException
+  {
+    String line;
+
+    /*
+     * parse location, which can be over >1 line e.g. EAW51554
+     */
+    CdsData data = new CdsData();
+    StringBuilder sb = new StringBuilder().append(location);
+    line = parseFeatureQualifier(sb, false);
+    data.cdsLocation = sb.toString();
+
+    while (line != null)
+    {
+      if (!isFeatureContinuationLine(line))
+      {
+        // e.g. start of next feature "FT source..."
+        break;
+      }
+
+      /*
+       * extract qualifier, e.g. FT    /protein_id="CAA37824.1"
+       * - the value may extend over more than one line
+       * - if the value has enclosing quotes, these are removed
+       * - escaped double quotes ("") are reduced to a single character
+       */
+      int slashPos = line.indexOf('/');
+      if (slashPos == -1)
+      {
+        Cache.log.error("Unexpected EMBL line ignored: " + line);
+        line = nextLine();
+        continue;
+      }
+      int eqPos = line.indexOf('=', slashPos + 1);
+      if (eqPos == -1)
+      {
+        // can happen, e.g. /ribosomal_slippage
+        line = nextLine();
+        continue;
+      }
+      String qualifier = line.substring(slashPos + 1, eqPos);
+      String value = line.substring(eqPos + 1);
+      value = removeQuotes(value);
+      sb = new StringBuilder().append(value);
+      boolean asText = !"translation".equals(qualifier);
+      line = parseFeatureQualifier(sb, asText);
+      String featureValue = sb.toString();
+
+      if ("protein_id".equals(qualifier))
+      {
+        data.proteinId = featureValue;
+      }
+      else if ("codon_start".equals(qualifier))
+      {
+        try
+        {
+          data.codonStart = Integer.parseInt(featureValue.trim());
+        } catch (NumberFormatException e)
+        {
+          Cache.log.error("Invalid codon_start in XML for " + this.accession
+                  + ": " + e.getMessage());
+        }
+      }
+      else if ("db_xref".equals(qualifier))
+      {
+        String[] parts = featureValue.split(":");
+        if (parts.length == 2)
+        {
+          String db = parts[0].trim();
+          db = DBRefUtils.getCanonicalName(db);
+          DBRefEntry dbref = new DBRefEntry(db, "0", parts[1].trim());
+          data.xrefs.add(dbref);
+        }
+      }
+      else if ("product".equals(qualifier))
+      {
+        data.proteinName = featureValue;
+      }
+      else if ("translation".equals(qualifier))
+      {
+        data.translation = featureValue;
+      }
+      else if (!"".equals(featureValue))
+      {
+        // throw anything else into the additional properties hash
+        data.cdsProps.put(qualifier, featureValue);
+      }
+    }
+
+    if (data.proteinId != null)
+    {
+      this.cds.put(data.proteinId, data);
+    }
+    else
+    {
+      Cache.log.error("Ignoring CDS feature with no protein_id for "
+              + sourceDb + ":" + accession);
+    }
+
+    return line;
+  }
+
+  protected abstract boolean isFeatureContinuationLine(String line);
+
+  /**
+   * Output (print) is not (yet) implemented for flat file format
+   */
+  @Override
+  public String print(SequenceI[] seqs, boolean jvsuffix)
+  {
+    return null;
+  }
+
+  /**
+   * Constructs and saves the sequence from parsed components
+   */
+  protected void buildSequence()
+  {
+    if (this.accession == null || this.sequenceString == null)
+    {
+      Cache.log.error("Failed to parse data from EMBL");
+      return;
+    }
+
+    String name = this.accession;
+    if (this.sourceDb != null)
+    {
+      name = this.sourceDb + "|" + name;
+    }
+
+    if (produceRna && sequenceStringIsRNA)
+    {
+      sequenceString = sequenceString.replace('T', 'U').replace('t', 'u');
+    }
+
+    SequenceI seq = new Sequence(name, this.sequenceString);
+    seq.setDescription(this.description);
+
+    /*
+     * add a DBRef to itself
+     */
+    DBRefEntry selfRef = new DBRefEntry(sourceDb, version, accession);
+    int[] startEnd = new int[] { 1, seq.getLength() };
+    selfRef.setMap(new Mapping(null, startEnd, startEnd, 1, 1));
+    seq.addDBRef(selfRef);
+
+    for (DBRefEntry dbref : this.dbrefs)
+    {
+      seq.addDBRef(dbref);
+    }
+
+    processCDSFeatures(seq);
+
+    seq.deriveSequence();
+
+    addSequence(seq);
+  }
+
+  /**
+   * Process the CDS features, including generation of cross-references and
+   * mappings to the protein products (translation)
+   * 
+   * @param seq
+   */
+  protected void processCDSFeatures(SequenceI seq)
+  {
+    /*
+     * record protein products found to avoid duplication i.e. >1 CDS with 
+     * the same /protein_id [though not sure I can find an example of this]
+     */
+    Map<String, SequenceI> proteins = new HashMap<>();
+    for (CdsData data : cds.values())
+    {
+      processCDSFeature(seq, data, proteins);
+    }
+  }
+
+  /**
+   * Processes data for one parsed CDS feature to
+   * <ul>
+   * <li>create a protein product sequence for the translation</li>
+   * <li>create a cross-reference to protein with mapping from dna</li>
+   * <li>add a CDS feature to the sequence for each CDS start-end range</li>
+   * <li>add any CDS dbrefs to the sequence and to the protein product</li>
+   * </ul>
+   * 
+   * @param SequenceI
+   *          dna
+   * @param proteins
+   *          map of protein products so far derived from CDS data
+   */
+  void processCDSFeature(SequenceI dna, CdsData data,
+          Map<String, SequenceI> proteins)
+  {
+    /*
+     * parse location into a list of [start, end, start, end] positions
+     */
+    int[] exons = getCdsRanges(this.accession, data.cdsLocation);
+
+    MapList maplist = buildMappingToProtein(dna, exons, data);
+
+    int exonNumber = 0;
+
+    for (int xint = 0; exons != null && xint < exons.length - 1; xint += 2)
+    {
+      int exonStart = exons[xint];
+      int exonEnd = exons[xint + 1];
+      int begin = Math.min(exonStart, exonEnd);
+      int end = Math.max(exonStart, exonEnd);
+      exonNumber++;
+      String desc = String.format("Exon %d for protein EMBLCDS:%s",
+              exonNumber, data.proteinId);
+
+      SequenceFeature sf = new SequenceFeature("CDS", desc, begin, end,
+              this.sourceDb);
+      for (Entry<String, String> val : data.cdsProps.entrySet())
+      {
+        sf.setValue(val.getKey(), val.getValue());
+      }
+
+      sf.setEnaLocation(data.cdsLocation);
+      boolean forwardStrand = exonStart <= exonEnd;
+      sf.setStrand(forwardStrand ? "+" : "-");
+      sf.setPhase(String.valueOf(data.codonStart - 1));
+      sf.setValue(FeatureProperties.EXONPOS, exonNumber);
+      sf.setValue(FeatureProperties.EXONPRODUCT, data.proteinName);
+
+      dna.addSequenceFeature(sf);
+    }
+
+    boolean hasUniprotDbref = false;
+    for (DBRefEntry xref : data.xrefs)
+    {
+      dna.addDBRef(xref);
+      if (xref.getSource().equals(DBRefSource.UNIPROT))
+      {
+        /*
+         * construct (or find) the sequence for (data.protein_id, data.translation)
+         */
+        SequenceI protein = buildProteinProduct(dna, xref, data, proteins);
+        Mapping map = new Mapping(protein, maplist);
+        map.setMappedFromId(data.proteinId);
+        xref.setMap(map);
+
+        /*
+         * add DBRefs with mappings from dna to protein and the inverse
+         */
+        DBRefEntry db1 = new DBRefEntry(sourceDb, version, accession);
+        db1.setMap(new Mapping(dna, maplist.getInverse()));
+        protein.addDBRef(db1);
+
+        hasUniprotDbref = true;
+      }
+    }
+
+    /*
+     * if we have a product (translation) but no explicit Uniprot dbref
+     * (example: EMBL M19487 protein_id AAB02592.1)
+     * then construct mappings to an assumed EMBLCDSPROTEIN accession
+     */
+    if (!hasUniprotDbref)
+    {
+      SequenceI protein = proteins.get(data.proteinId);
+      if (protein == null)
+      {
+        protein = new Sequence(data.proteinId, data.translation);
+        protein.setDescription(data.proteinName);
+        proteins.put(data.proteinId, protein);
+      }
+      // assuming CDSPROTEIN sequence version = dna version (?!)
+      DBRefEntry db1 = new DBRefEntry(DBRefSource.EMBLCDSProduct,
+              this.version, data.proteinId);
+      protein.addDBRef(db1);
+
+      DBRefEntry dnaToEmblProteinRef = new DBRefEntry(
+              DBRefSource.EMBLCDSProduct, this.version, data.proteinId);
+      Mapping map = new Mapping(protein, maplist);
+      map.setMappedFromId(data.proteinId);
+      dnaToEmblProteinRef.setMap(map);
+      dna.addDBRef(dnaToEmblProteinRef);
+    }
+
+    /*
+     * comment brought forward from EmblXmlSource, lines 447-451:
+     * TODO: if retrieved from EMBLCDS, add a DBRef back to the parent EMBL
+     * sequence with the exon  map; if given a dataset reference, search
+     * dataset for parent EMBL sequence if it exists and set its map;
+     * make a new feature annotating the coding contig
+     */
+  }
+
+  /**
+   * Computes a mapping from CDS positions in DNA sequence to protein product
+   * positions, with allowance for stop codon or incomplete start codon
+   * 
+   * @param dna
+   * @param exons
+   * @param data
+   * @return
+   */
+  MapList buildMappingToProtein(final SequenceI dna, final int[] exons,
+          final CdsData data)
+  {
+    MapList dnaToProteinMapping = null;
+    int peptideLength = data.translation.length();
+
+    int[] proteinRange = new int[] { 1, peptideLength };
+    if (exons != null && exons.length > 0)
+    {
+      /*
+       * We were able to parse 'location'; do a final 
+       * product length truncation check
+       */
+      int[] cdsRanges = adjustForProteinLength(peptideLength, exons);
+      dnaToProteinMapping = new MapList(cdsRanges, proteinRange, 3, 1);
+    }
+    else
+    {
+      /*
+       * workaround until we handle all 'location' formats fully
+       * e.g. X53828.1:60..1058 or <123..>289
+       */
+      Cache.log.error(String.format(
+              "Implementation Notice: EMBLCDS location '%s'not properly supported yet"
+                      + " - Making up the CDNA region of (%s:%s)... may be incorrect",
+              data.cdsLocation, sourceDb, this.accession));
+
+      int completeCodonsLength = 1 - data.codonStart + dna.getLength();
+      int mappedDnaEnd = dna.getEnd();
+      if (peptideLength * 3 == completeCodonsLength)
+      {
+        // this might occur for CDS sequences where no features are marked
+        Cache.log.warn("Assuming no stop codon at end of cDNA fragment");
+        mappedDnaEnd = dna.getEnd();
+      }
+      else if ((peptideLength + 1) * 3 == completeCodonsLength)
+      {
+        Cache.log.warn("Assuming stop codon at end of cDNA fragment");
+        mappedDnaEnd = dna.getEnd() - 3;
+      }
+
+      if (mappedDnaEnd != -1)
+      {
+        int[] cdsRanges = new int[] {
+            dna.getStart() + (data.codonStart - 1), mappedDnaEnd };
+        dnaToProteinMapping = new MapList(cdsRanges, proteinRange, 3, 1);
+      }
+    }
+
+    return dnaToProteinMapping;
+  }
+
+  /**
+   * Constructs a sequence for the protein product for the CDS data (if there is
+   * one), and dbrefs with mappings from CDS to protein and the reverse
+   * 
+   * @param dna
+   * @param xref
+   * @param data
+   * @param proteins
+   * @return
+   */
+  SequenceI buildProteinProduct(SequenceI dna, DBRefEntry xref,
+          CdsData data, Map<String, SequenceI> proteins)
+  {
+    /*
+     * check we have some data to work with
+     */
+    if (data.proteinId == null || data.translation == null)
+    {
+      return null;
+    }
+
+    /*
+     * Construct the protein sequence (if not already seen)
+     */
+    String proteinSeqName = xref.getSource() + "|" + xref.getAccessionId();
+    SequenceI protein = proteins.get(proteinSeqName);
+    if (protein == null)
+    {
+      protein = new Sequence(proteinSeqName, data.translation, 1,
+              data.translation.length());
+      protein.setDescription(data.proteinName != null ? data.proteinName
+              : "Protein Product from " + sourceDb);
+      proteins.put(proteinSeqName, protein);
+    }
+
+    return protein;
+  }
+
+  /**
+   * Returns the CDS location as a single array of [start, end, start, end...]
+   * positions. If on the reverse strand, these will be in descending order.
+   * 
+   * @param accession
+   * @param location
+   * @return
+   */
+  protected int[] getCdsRanges(String accession, String location)
+  {
+    if (location == null)
+    {
+      return new int[] {};
+    }
+
+    try
+    {
+      List<int[]> ranges = DnaUtils.parseLocation(location);
+      return MappingUtils.rangeListToArray(ranges);
+    } catch (ParseException e)
+    {
+      Cache.log.warn(
+              String.format("Not parsing inexact CDS location %s in ENA %s",
+                      location, accession));
+      return new int[] {};
+    }
+  }
+
+  /**
+   * Reads the value of a feature (FT) qualifier from one or more lines of the
+   * file, and returns the next line after that. Values are appended to the
+   * string buffer, which should be already primed with the value read from the
+   * first line for the qualifier (with any leading double quote removed).
+   * Enclosing double quotes are removed, and escaped (repeated) double quotes
+   * reduced to one only. For example for
+   * 
+   * <pre>
+   * FT      /note="gene_id=hCG28070.3 
+   * FT      ""foobar"" isoform=CRA_b"
+   * the returned value is
+   * gene_id=hCG28070.3 "foobar" isoform=CRA_b
+   * </pre>
+   * 
+   * Note the side-effect of this method, to advance data reading to the next
+   * line after the feature qualifier (which could be another qualifier, a
+   * different feature, a non-feature line, or null at end of file).
+   * 
+   * @param sb
+   *          a string buffer primed with the first line of the value
+   * @param asText
+   * @return
+   * @throws IOException
+   */
+  String parseFeatureQualifier(StringBuilder sb, boolean asText)
+          throws IOException
+  {
+    String line;
+    while ((line = nextLine()) != null)
+    {
+      if (!isFeatureContinuationLine(line))
+      {
+        break; // reached next feature or other input line
+      }
+      String[] tokens = line.split(WHITESPACE);
+      if (tokens.length < 2)
+      {
+        Cache.log.error("Ignoring bad EMBL line for " + this.accession
+                + ": " + line);
+        break;
+      }
+      if (tokens[1].startsWith("/"))
+      {
+        break; // next feature qualifier
+      }
+
+      /*
+       * if text (e.g. /product), add a word separator for a new line,
+       * else (e.g. /translation) don't
+       */
+      if (asText)
+      {
+        sb.append(" ");
+      }
+
+      /*
+       * remove trailing " and unescape doubled ""
+       */
+      String data = removeQuotes(tokens[1]);
+      sb.append(data);
+    }
+
+    return line;
+  }
+
+  /**
+   * Reads and saves the sequence, read from the lines following the ORIGIN
+   * (GenBank) or SQ (EMBL) line. Whitespace and position counters are
+   * discarded. Returns the next line following the sequence data (the next line
+   * that doesn't start with whitespace).
+   * 
+   * @throws IOException
+   */
+  protected String parseSequence() throws IOException
+  {
+    StringBuilder sb = new StringBuilder(this.length);
+    String line = nextLine();
+    while (line != null && line.startsWith(" "))
+    {
+      line = line.trim();
+      String[] blocks = line.split(WHITESPACE);
+
+      /*
+       * the first or last block on each line might be a position count - omit
+       */
+      for (int i = 0; i < blocks.length; i++)
+      {
+        try
+        {
+          Long.parseLong(blocks[i]);
+          // position counter - ignore it
+        } catch (NumberFormatException e)
+        {
+          // sequence data - append it
+          sb.append(blocks[i]);
+        }
+      }
+      line = nextLine();
+    }
+    this.sequenceString = sb.toString();
+
+    return line;
+  }
+
+  /**
+   * Processes a feature line. If it declares a feature type of interest
+   * (currently, only CDS is processed), processes all of the associated lines
+   * (feature qualifiers), and returns the next line after that, otherwise
+   * simply returns the next line.
+   * 
+   * @param line
+   *          the first line for the feature (with initial FT omitted for EMBL
+   *          format)
+   * @return
+   * @throws IOException
+   */
+  protected String parseFeature(String line) throws IOException
+  {
+    String[] tokens = line.trim().split(WHITESPACE);
+    if (tokens.length < 2 || (!"CDS".equals(tokens[0]) && (!"source".equals(tokens[0]))))
+    {
+      return nextLine();
+    }
+    if (tokens[0].equals("source"))
+    {
+      return parseSourceQualifiers(tokens);
+    }
+    return parseCDSFeature(tokens[1]);
+  }
+}
+
+/**
+ * A data bean class to hold values parsed from one CDS Feature
+ */
+class CdsData
+{
+  String translation; // from /translation qualifier
+
+  String cdsLocation; // the raw value e.g. join(1..1234,2012..2837)
+
+  int codonStart = 1; // from /codon_start qualifier
+
+  String proteinName; // from /product qualifier; used for protein description
+
+  String proteinId; // from /protein_id qualifier
+
+  List<DBRefEntry> xrefs = new ArrayList<>(); // from /db_xref qualifiers
+
+  Map<String, String> cdsProps = new Hashtable<>(); // other qualifiers
+}
diff --git a/src/jalview/io/EmblFlatFile.java b/src/jalview/io/EmblFlatFile.java
new file mode 100644 (file)
index 0000000..7808d1a
--- /dev/null
@@ -0,0 +1,209 @@
+package jalview.io;
+
+import java.io.IOException;
+
+import jalview.bin.Cache;
+import jalview.datamodel.DBRefEntry;
+import jalview.util.DBRefUtils;
+
+/**
+ * A class that provides selective parsing of the EMBL flatfile format.
+ * <p>
+ * The initial implementation is limited to extracting fields used by Jalview
+ * after fetching an EMBL or EMBLCDS entry:
+ * 
+ * <pre>
+ * accession, version, sequence, xref
+ * and (for CDS feature) location, protein_id, product, codon_start, translation
+ * </pre>
+ * 
+ * For a complete parser, it may be best to adopt that provided in
+ * https://github.com/enasequence/sequencetools/tree/master/src/main/java/uk/ac/ebi/embl/flatfile
+ * (but note this has a dependency on the Apache Commons library)
+ * 
+ * @author gmcarstairs
+ * @see ftp://ftp.ebi.ac.uk/pub/databases/ena/sequence/release/doc/usrman.txt
+ * @see ftp://ftp.ebi.ac.uk/pub/databases/embl/doc/FT_current.html
+ */
+public class EmblFlatFile extends EMBLLikeFlatFile
+{
+  /**
+   * Constructor given a data source and the id of the source database
+   * 
+   * @param fp
+   * @param sourceId
+   * @throws IOException
+   */
+  public EmblFlatFile(FileParse fp, String sourceId) throws IOException
+  {
+    super(fp, sourceId);
+  }
+
+  /**
+   * Parses the flatfile, and if successful, saves as an annotated sequence
+   * which may be retrieved by calling {@code getSequence()}
+   * 
+   * @throws IOException
+   */
+  @Override
+  public void parse() throws IOException
+  {
+    String line = nextLine();
+    while (line != null)
+    {
+      if (line.startsWith("ID"))
+      {
+        line = parseID(line);
+      }
+      else if (line.startsWith("DE"))
+      {
+        line = parseDE(line);
+      }
+      else if (line.startsWith("DR"))
+      {
+        line = parseDR(line);
+      }
+      else if (line.startsWith("SQ"))
+      {
+        line = parseSequence();
+      }
+      else if (line.startsWith("FT"))
+      {
+        line = parseFeature(line.substring(2));
+      }
+      else
+      {
+        line = nextLine();
+      }
+    }
+    buildSequence();
+  }
+
+  /**
+   * Extracts and saves the primary accession and version (SV value) from an ID
+   * line, or null if not found. Returns the next line after the one processed.
+   * 
+   * @param line
+   * @throws IOException
+   */
+  String parseID(String line) throws IOException
+  {
+    String[] tokens = line.substring(2).split(";");
+
+    /*
+     * first is primary accession
+     */
+    String token = tokens[0].trim();
+    if (!token.isEmpty())
+    {
+      this.accession = token;
+    }
+
+    /*
+     * second token is 'SV versionNo'
+     */
+    if (tokens.length > 1)
+    {
+      token = tokens[1].trim();
+      if (token.startsWith("SV"))
+      {
+        String[] bits = token.trim().split(WHITESPACE);
+        this.version = bits[bits.length - 1];
+      }
+    }
+
+    /*
+     * seventh token is 'length BP'
+     */
+    if (tokens.length > 6)
+    {
+      token = tokens[6].trim();
+      String[] bits = token.trim().split(WHITESPACE);
+      try
+      {
+        this.length = Integer.valueOf(bits[0]);
+      } catch (NumberFormatException e)
+      {
+        Cache.log.error("bad length read in flatfile, line: " + line);
+      }
+    }
+
+    return nextLine();
+  }
+
+  /**
+   * Reads sequence description from the first DE line found. Any trailing
+   * period is discarded. If there are multiple DE lines, only the first (short
+   * description) is read, the rest are ignored.
+   * 
+   * @param line
+   * @return
+   * @throws IOException
+   */
+  String parseDE(String line) throws IOException
+  {
+    String desc = line.substring(2).trim();
+    if (desc.endsWith("."))
+    {
+      desc = desc.substring(0, desc.length() - 1);
+    }
+    this.description = desc;
+
+    /*
+     * pass over any additional DE lines
+     */
+    while ((line = nextLine()) != null)
+    {
+      if (!line.startsWith("DE"))
+      {
+        break;
+      }
+    }
+
+    return line;
+  }
+
+  /**
+   * Processes one DR line and saves as a DBRefEntry cross-reference. Returns
+   * the line following the line processed.
+   * 
+   * @param line
+   * @throws IOException
+   */
+  String parseDR(String line) throws IOException
+  {
+    String[] tokens = line.substring(2).split(";");
+    if (tokens.length > 1)
+    {
+      /*
+       * ensure UniProtKB/Swiss-Prot converted to UNIPROT
+       */
+      String db = tokens[0].trim();
+      db = DBRefUtils.getCanonicalName(db);
+      String acc = tokens[1].trim();
+      if (acc.endsWith("."))
+      {
+        acc = acc.substring(0, acc.length() - 1);
+      }
+      String version = "0";
+      if (tokens.length > 2)
+      {
+        String secondaryId = tokens[2].trim();
+        if (!secondaryId.isEmpty())
+        {
+          // todo: is this right? secondary id is not a version number
+          // version = secondaryId;
+        }
+      }
+      this.dbrefs.add(new DBRefEntry(db, version, acc));
+    }
+
+    return nextLine();
+  }
+
+  @Override
+  protected boolean isFeatureContinuationLine(String line)
+  {
+    return line.startsWith("FT    "); // 4 spaces
+  }
+}
index c08c84e..dda59a7 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import java.awt.Color;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -234,7 +236,7 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
         // skip comments/process pragmas
         if (line.length() == 0 || line.startsWith("#"))
         {
-          if (line.toLowerCase().startsWith("##"))
+          if (line.toLowerCase(Locale.ROOT).startsWith("##"))
           {
             processGffPragma(line, gffProps, align, newseqs);
           }
@@ -346,7 +348,7 @@ public class FeaturesFile extends AlignFile implements FeaturesSourceI
     String line;
     while ((line = nextLine()) != null)
     {
-      if (line.toUpperCase().startsWith(ENDFILTERS))
+      if (line.toUpperCase(Locale.ROOT).startsWith(ENDFILTERS))
       {
         return;
       }
index cb61740..a743694 100644 (file)
  */
 package jalview.io;
 
+import java.io.IOException;
+
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefSource;
 import jalview.datamodel.PDBEntry;
 import jalview.ext.jmol.JmolParser;
 import jalview.structure.StructureImportSettings;
 
-import java.io.IOException;
-
 public enum FileFormat implements FileFormatI
 {
   Fasta("Fasta", "fa, fasta, mfa, fastq", true, true)
@@ -243,6 +244,37 @@ public enum FileFormat implements FileFormatI
       return new PhylipFile();
     }
   },
+  GenBank("GenBank Flatfile", "gb, gbk", true, false)
+  {
+    @Override
+    public AlignmentFileReaderI getReader(FileParse source)
+            throws IOException
+    {
+      return new GenBankFile(source, "GenBank");
+    }
+
+    @Override
+    public AlignmentFileWriterI getWriter(AlignmentI al)
+    {
+      return null;
+    }
+  },
+  Embl("ENA Flatfile", "txt", true, false)
+  {
+    @Override
+    public AlignmentFileReaderI getReader(FileParse source)
+            throws IOException
+    {
+      // Always assume we import from EMBL for now
+      return new EmblFlatFile(source, DBRefSource.EMBL);
+    }
+
+    @Override
+    public AlignmentFileWriterI getWriter(AlignmentI al)
+    {
+      return null;
+    }
+  },
   Jnet("JnetFile", "", false, false)
   {
     @Override
@@ -407,8 +439,8 @@ public enum FileFormat implements FileFormatI
    * @param shortName
    * @param extensions
    *          comma-separated list of file extensions associated with the format
-   * @param isReadable
-   * @param isWritable
+   * @param isReadable - can be recognised by IdentifyFile and imported with the given reader
+   * @param isWritable - can be exported with the returned writer
    */
   private FileFormat(String shortName, String extensions,
           boolean isReadable, boolean isWritable)
index aadcdb9..9e0a7f7 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -102,7 +104,7 @@ public class FileFormats
   protected void registerFileFormat(FileFormatI format,
           boolean isIdentifiable)
   {
-    String name = format.getName().toUpperCase();
+    String name = format.getName().toUpperCase(Locale.ROOT);
     if (formats.containsKey(name))
     {
       System.err.println("Overwriting file format: " + format.getName());
@@ -121,7 +123,7 @@ public class FileFormats
    */
   public void deregisterFileFormat(String name)
   {
-    FileFormatI ff = formats.remove(name.toUpperCase());
+    FileFormatI ff = formats.remove(name.toUpperCase(Locale.ROOT));
     identifiable.remove(ff);
   }
 
@@ -174,7 +176,7 @@ public class FileFormats
    */
   public FileFormatI forName(String format)
   {
-    return format == null ? null : formats.get(format.toUpperCase());
+    return format == null ? null : formats.get(format.toUpperCase(Locale.ROOT));
   }
 
   /**
index 3aa433e..c7c1d71 100755 (executable)
@@ -416,12 +416,9 @@ public class FileLoader implements Runnable
                   .getFeatureColourScheme();
           if (viewport != null)
           {
-            if (proxyColourScheme != null)
-            {
-              viewport.applyFeaturesStyle(proxyColourScheme);
-            }
             // append to existing alignment
             viewport.addAlignment(al, title);
+            viewport.applyFeaturesStyle(proxyColourScheme);
           }
           else
           {
index 85cf48a..31751f5 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import jalview.api.AlignExportSettingsI;
 import jalview.api.AlignmentViewPanel;
 import jalview.bin.Cache;
@@ -162,7 +164,7 @@ public class FormatAdapter extends AppletFormatAdapter
 
   public boolean getCacheSuffixDefault(FileFormatI format)
   {
-    return Cache.getDefault(format.getName().toUpperCase() + "_JVSUFFIX",
+    return Cache.getDefault(format.getName().toUpperCase(Locale.ROOT) + "_JVSUFFIX",
             true);
   }
 
diff --git a/src/jalview/io/GenBankFile.java b/src/jalview/io/GenBankFile.java
new file mode 100644 (file)
index 0000000..f1ca0e3
--- /dev/null
@@ -0,0 +1,189 @@
+package jalview.io;
+
+import java.io.IOException;
+
+/**
+ * A class that provides selective parsing of the GenBank flatfile format.
+ * <p>
+ * The initial implementation is limited to extracting fields used by Jalview
+ * after fetching an EMBL or EMBLCDS entry:
+ * 
+ * <pre>
+ * accession, version, sequence, xref
+ * and (for CDS feature) location, protein_id, product, codon_start, translation
+ * </pre>
+ * 
+ * @author gmcarstairs
+ * @see https://www.ncbi.nlm.nih.gov/Sitemap/samplerecord.html
+ */
+public class GenBankFile extends EMBLLikeFlatFile
+{
+  private static final String DEFINITION = "DEFINITION";
+
+  /**
+   * Constructor given a data source and the id of the source database
+   * 
+   * @param fp
+   * @param sourceId
+   * @throws IOException
+   */
+  public GenBankFile(FileParse fp, String sourceId) throws IOException
+  {
+    super(fp, sourceId);
+  }
+
+  /**
+   * Parses the flatfile, and if successful, saves as an annotated sequence
+   * which may be retrieved by calling {@code getSequence()}
+   * 
+   * @throws IOException
+   * @see https://www.ncbi.nlm.nih.gov/Sitemap/samplerecord.html
+   */
+  @Override
+  public void parse() throws IOException
+  {
+    String line = nextLine();
+    while (line != null)
+    {
+      if (line.startsWith("LOCUS"))
+      {
+        line = parseLocus(line);
+      }
+      else if (line.startsWith(DEFINITION))
+      {
+        line = parseDefinition(line);
+      }
+      else if (line.startsWith("ACCESSION"))
+      {
+        this.accession = line.split(WHITESPACE)[1];
+        line = nextLine();
+      }
+      else if (line.startsWith("VERSION"))
+      {
+        line = parseVersion(line);
+      }
+      else if (line.startsWith("ORIGIN"))
+      {
+        line = parseSequence();
+      }
+      else if (line.startsWith("FEATURES"))
+      {
+        line = nextLine();
+        while (line.startsWith(" "))
+        {
+          line = parseFeature(line);
+        }
+      }
+      else
+      {
+        line = nextLine();
+      }
+    }
+    buildSequence();
+  }
+
+  /**
+   * Extracts and saves the primary accession and version (SV value) from an ID
+   * line, or null if not found. Returns the next line after the one processed.
+   * 
+   * @param line
+   * @throws IOException
+   */
+  String parseLocus(String line) throws IOException
+  {
+    String[] tokens = line.split(WHITESPACE);
+
+    /*
+     * first should be "LOCUS"
+     */
+    if (tokens.length < 2 || !"LOCUS".equals(tokens[0]))
+    {
+      return nextLine();
+    }
+    /*
+     * second is primary accession
+     */
+    String token = tokens[1].trim();
+    if (!token.isEmpty())
+    {
+      this.accession = token;
+    }
+
+    // not going to guess the rest just yet, but third is length with unit (bp)
+
+    return nextLine();
+  }
+
+  /**
+   * Reads sequence description from DEFINITION lines. Any trailing period is
+   * discarded. Returns the next line after the definition line(s).
+   * 
+   * @param line
+   * @return
+   * @throws IOException
+   */
+  String parseDefinition(String line) throws IOException
+  {
+    String desc = line.substring(DEFINITION.length()).trim();
+    if (desc.endsWith("."))
+    {
+      desc = desc.substring(0, desc.length() - 1);
+    }
+
+    /*
+     * pass over any additional DE lines
+     */
+    while ((line = nextLine()) != null)
+    {
+      if (line.startsWith(" "))
+      {
+        // definition continuation line
+        desc += line.trim();
+      }
+      else
+      {
+        break;
+      }
+    }
+    this.description = desc;
+
+    return line;
+  }
+
+  /**
+   * Parses the VERSION line e.g.
+   * 
+   * <pre>
+   * VERSION     X81322.1
+   * </pre>
+   * 
+   * and returns the next line
+   * 
+   * @param line
+   * @throws IOException
+   */
+  String parseVersion(String line) throws IOException
+  {
+    /*
+     * extract version part of <accession>.<version>
+     * https://www.ncbi.nlm.nih.gov/Sitemap/samplerecord.html#VersionB
+     */
+    String[] tokens = line.split(WHITESPACE);
+    if (tokens.length > 1)
+    {
+      tokens = tokens[1].split("\\.");
+      if (tokens.length > 1)
+      {
+        this.version = tokens[1];
+      }
+    }
+
+    return nextLine();
+  }
+
+  @Override
+  protected boolean isFeatureContinuationLine(String line)
+  {
+    return line.startsWith("      "); // 6 spaces
+  }
+}
index b312474..5a3d700 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import java.io.File;
 import java.io.IOException;
 
@@ -178,13 +180,26 @@ public class IdentifyFile
             break;
           }
         }
-        data = data.toUpperCase();
+        data = data.toUpperCase(Locale.ROOT);
 
         if (data.startsWith(ScoreMatrixFile.SCOREMATRIX))
         {
           reply = FileFormat.ScoreMatrix;
           break;
         }
+        if (data.startsWith("LOCUS"))
+        {
+          reply = FileFormat.GenBank;
+          break;
+        }
+        if (data.startsWith("ID "))
+        {
+          if (data.substring(2).trim().split(";").length == 7)
+          {
+            reply = FileFormat.Embl;
+            break;
+          }
+        }
         if (data.startsWith("H ") && !aaIndexHeaderRead)
         {
           aaIndexHeaderRead = true;
@@ -319,7 +334,7 @@ public class IdentifyFile
         if ((lessThan > -1)) // possible Markup Language data i.e HTML,
                              // RNAML, XML
         {
-          String upper = data.toUpperCase();
+          String upper = data.toUpperCase(Locale.ROOT);
           if (upper.substring(lessThan).startsWith("<HTML"))
           {
             reply = FileFormat.Html;
index 6af92b7..ab2c00a 100755 (executable)
@@ -25,6 +25,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Sequence;
@@ -409,7 +411,7 @@ public class JPredFile extends AlignFile
     // check that no stray annotations have been added at the end.
     {
       SequenceI sq = seqs.elementAt(j - 1);
-      if (sq.getName().toUpperCase().startsWith("JPRED"))
+      if (sq.getName().toUpperCase(Locale.ROOT).startsWith("JPRED"))
       {
         annotSeqs.addElement(sq);
         seqs.removeElementAt(--j);
index e0f28bb..022148a 100644 (file)
@@ -70,7 +70,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile
 {
   private static String version = new BuildDetails().getVersion();
 
-  private String webstartUrl = "http://www.jalview.org/services/launchApp";
+  private String webstartUrl = "https://www.jalview.org/services/launchApp";
 
   private String application = "Jalview";
 
index bc20342..28dd2eb 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import java.io.File;
 import java.util.Hashtable;
 import java.util.Iterator;
@@ -134,7 +136,7 @@ public class JalviewFileFilter extends FileFilter
 
       if ((i > 0) && (i < (filename.length() - 1)))
       {
-        return filename.substring(i + 1).toLowerCase();
+        return filename.substring(i + 1).toLowerCase(Locale.ROOT);
       }
 
       ;
@@ -150,7 +152,7 @@ public class JalviewFileFilter extends FileFilter
       filters = new LinkedHashMap<>(5);
     }
 
-    filters.put(extension.toLowerCase(), this);
+    filters.put(extension.toLowerCase(Locale.ROOT), this);
     fullDescription = null;
   }
 
index 52d130c..b90bd7a 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import jalview.util.MessageManager;
 
 import java.io.File;
@@ -47,7 +49,7 @@ public class JalviewFileView extends FileView
       String exts = ff.getExtensions();
       for (String ext : exts.split(","))
       {
-        ext = ext.trim().toLowerCase();
+        ext = ext.trim().toLowerCase(Locale.ROOT);
         extensions.put(ext,
                 desc + ("jar".equals(ext) ? " (old)" : ""));
       }
@@ -136,7 +138,7 @@ public class JalviewFileView extends FileView
 
     if ((i > 0) && (i < (s.length() - 1)))
     {
-      ext = s.substring(i + 1).toLowerCase();
+      ext = s.substring(i + 1).toLowerCase(Locale.ROOT);
     }
 
     return ext;
index 6828202..27fc869 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
@@ -88,7 +90,7 @@ public class JnetAnnotationMaker
 
     while (i < preds.length)
     {
-      String id = preds[i].getName().toUpperCase();
+      String id = preds[i].getName().toUpperCase(Locale.ROOT);
 
       if (id.startsWith("LUPAS") || id.startsWith("JNET")
               || id.startsWith("JPRED"))
index df2bed2..e954703 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.util.Comparison;
@@ -184,7 +186,7 @@ public class MSFfile extends AlignFile
   public int checkSum(String seq)
   {
     int check = 0;
-    String sequence = seq.toUpperCase();
+    String sequence = seq.toUpperCase(Locale.ROOT);
 
     for (int i = 0; i < sequence.length(); i++)
     {
index f3eaa45..ec5d267 100755 (executable)
@@ -26,6 +26,8 @@
 // TODO: Extended SequenceNodeI to hold parsed NHX strings
 package jalview.io;
 
+import java.util.Locale;
+
 import jalview.datamodel.SequenceNode;
 import jalview.util.MessageManager;
 
@@ -656,7 +658,7 @@ public class NewickFile extends FileParse
           try
           {
             // parse out code/value pairs
-            if (code.toLowerCase().equals("b"))
+            if (code.toLowerCase(Locale.ROOT).equals("b"))
             {
               int v = -1;
               Float iv = Float.valueOf(value);
index 01610a1..6b09cd1 100644 (file)
@@ -36,10 +36,9 @@ public class PDBFeatureSettings extends FeatureSettingsAdapter
   private static final String FEATURE_RES_NUM = PDBChain.RESNUM_FEATURE;
 
   @Override
-  public boolean isFeatureDisplayed(String type)
+  public boolean isFeatureHidden(String type)
   {
-    return type.equalsIgnoreCase(FEATURE_INSERTION)
-            || type.equalsIgnoreCase(FEATURE_RES_NUM);
+    return type.equalsIgnoreCase(FEATURE_RES_NUM);
   }
 
   @Override
index 8e4e783..62b723d 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.LinkedHashMap;
@@ -273,7 +275,7 @@ public class SequenceAnnotationReport
          * truncate overlong descriptions unless they contain an href
          * before the truncation point (as truncation could leave corrupted html)
          */
-        int linkindex = description.toLowerCase().indexOf("<a ");
+        int linkindex = description.toLowerCase(Locale.ROOT).indexOf("<a ");
         boolean hasLink = linkindex > -1
                 && linkindex < MAX_DESCRIPTION_LENGTH;
         if (description.length() > MAX_DESCRIPTION_LENGTH && !hasLink)
@@ -400,8 +402,8 @@ public class SequenceAnnotationReport
                       + "\" target=\""
                       + urllink.get(0)
                       + "\">"
-                      + (urllink.get(0).toLowerCase()
-                              .equals(urllink.get(1).toLowerCase()) ? urllink
+                      + (urllink.get(0).toLowerCase(Locale.ROOT)
+                              .equals(urllink.get(1).toLowerCase(Locale.ROOT)) ? urllink
                               .get(0) : (urllink.get(0) + ":" + urllink
                                               .get(1)))
                       + "</a><br/>");
index 8b26757..c8c9c8a 100644 (file)
@@ -23,6 +23,8 @@
  */
 package jalview.io;
 
+import java.util.Locale;
+
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
@@ -1110,7 +1112,7 @@ public class StockholmFile extends AlignFile
         }
         else
         {
-          key = type2id(aa.label.toLowerCase());
+          key = type2id(aa.label.toLowerCase(Locale.ROOT));
           if (key == null)
           {
             label = aa.label;
index 084f886..94a832b 100644 (file)
@@ -119,6 +119,7 @@ public abstract class StructureFile extends AlignFile
     pdbSequence.setName(getId() + "|" + pdbSequence.getName());
     PDBEntry entry = new PDBEntry();
     entry.setId(getId());
+    entry.setFakedPDBId(!isPPDBIdAvailable());
     entry.setType(getStructureFileType());
     if (chain.id != null)
     {
index da0c245..9ce4cc6 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.io.gff;
 
+import java.util.Locale;
+
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.MappingType;
@@ -341,7 +343,7 @@ public class ExonerateHelper extends Gff2Helper
     // e.g. exonerate:protein2genome:local
     if (model != null)
     {
-      String mdl = model.toLowerCase();
+      String mdl = model.toLowerCase(Locale.ROOT);
       if (mdl.contains(PROTEIN2DNA) || mdl.contains(PROTEIN2GENOME)
               || mdl.contains(CODING2CODING) || mdl.contains(CODING2GENOME)
               || mdl.contains(CDNA2GENOME) || mdl.contains(GENOME2GENOME))
index d40446d..37dd66b 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.io.packed;
 
+import java.util.Locale;
+
 import jalview.api.FeatureColourI;
 import jalview.datamodel.AlignmentI;
 import jalview.io.AppletFormatAdapter;
@@ -235,7 +237,7 @@ public class ParsePackedSet
       String type = args[i++];
       final String file = args[i++];
       final JvDataType jtype = DataProvider.JvDataType
-              .valueOf(type.toUpperCase());
+              .valueOf(type.toUpperCase(Locale.ROOT));
       if (jtype != null)
       {
         final FileParse fp;
@@ -254,7 +256,7 @@ public class ParsePackedSet
       else
       {
         System.out.println("Couldn't parse source type token '"
-                + type.toUpperCase() + "'");
+                + type.toUpperCase(Locale.ROOT) + "'");
       }
     }
     if (i < args.length)
index f4ffc0c..dadb532 100644 (file)
  */
 package jalview.io.vcf;
 
-import jalview.analysis.Dna;
-import jalview.api.AlignViewControllerGuiI;
-import jalview.bin.Cache;
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.GeneLociI;
-import jalview.datamodel.Mapping;
-import jalview.datamodel.SequenceFeature;
-import jalview.datamodel.SequenceI;
-import jalview.datamodel.features.FeatureAttributeType;
-import jalview.datamodel.features.FeatureSource;
-import jalview.datamodel.features.FeatureSources;
-import jalview.ext.ensembl.EnsemblMap;
-import jalview.ext.htsjdk.HtsContigDb;
-import jalview.ext.htsjdk.VCFReader;
-import jalview.io.gff.Gff3Helper;
-import jalview.io.gff.SequenceOntologyI;
-import jalview.util.MapList;
-import jalview.util.MappingUtils;
-import jalview.util.MessageManager;
-import jalview.util.StringUtils;
+import java.util.Locale;
 
 import java.io.File;
 import java.io.IOException;
@@ -67,6 +48,26 @@ import htsjdk.variant.vcf.VCFHeaderLine;
 import htsjdk.variant.vcf.VCFHeaderLineCount;
 import htsjdk.variant.vcf.VCFHeaderLineType;
 import htsjdk.variant.vcf.VCFInfoHeaderLine;
+import jalview.analysis.Dna;
+import jalview.api.AlignViewControllerGuiI;
+import jalview.bin.Cache;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.GeneLociI;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.FeatureAttributeType;
+import jalview.datamodel.features.FeatureSource;
+import jalview.datamodel.features.FeatureSources;
+import jalview.ext.ensembl.EnsemblMap;
+import jalview.ext.htsjdk.HtsContigDb;
+import jalview.ext.htsjdk.VCFReader;
+import jalview.io.gff.Gff3Helper;
+import jalview.io.gff.SequenceOntologyI;
+import jalview.util.MapList;
+import jalview.util.MappingUtils;
+import jalview.util.MessageManager;
+import jalview.util.StringUtils;
 
 /**
  * A class to read VCF data (using the htsjdk) and add variants as sequence
@@ -432,7 +433,7 @@ public class VCFLoader
               + DEFAULT_REFERENCE + ":" + DEFAULT_SPECIES);
       reference = DEFAULT_REFERENCE; // default to GRCh37 if not specified
     }
-    reference = reference.toLowerCase();
+    reference = reference.toLowerCase(Locale.ROOT);
 
     /*
      * for a non-human species, or other assembly identifier,
@@ -447,7 +448,7 @@ public class VCFLoader
       String[] tokens = token.split("=");
       if (tokens.length == 2)
       {
-        if (reference.contains(tokens[0].trim().toLowerCase()))
+        if (reference.contains(tokens[0].trim().toLowerCase(Locale.ROOT)))
         {
           vcfAssembly = tokens[1].trim();
           break;
@@ -464,7 +465,7 @@ public class VCFLoader
         String[] tokens = token.split("=");
         if (tokens.length == 2)
         {
-          if (reference.contains(tokens[0].trim().toLowerCase()))
+          if (reference.contains(tokens[0].trim().toLowerCase(Locale.ROOT)))
           {
             vcfSpecies = tokens[1].trim();
             break;
@@ -569,7 +570,7 @@ public class VCFLoader
   {
     for (Pattern p : filters)
     {
-      if (p.matcher(id.toUpperCase()).matches())
+      if (p.matcher(id.toUpperCase(Locale.ROOT)).matches())
       {
         return true;
       }
@@ -663,7 +664,7 @@ public class VCFLoader
     {
       try
       {
-      patterns.add(Pattern.compile(token.toUpperCase()));
+      patterns.add(Pattern.compile(token.toUpperCase(Locale.ROOT)));
       } catch (PatternSyntaxException e)
       {
         System.err.println("Invalid pattern ignored: " + token);
@@ -674,7 +675,6 @@ public class VCFLoader
 
   /**
    * Transfers VCF features to sequences to which this sequence has a mapping.
-   * If the mapping is 3:1, computes peptide variants from nucleotide variants.
    * 
    * @param seq
    */
@@ -897,12 +897,19 @@ public class VCFLoader
           int[] featureRange = map.map.locateInFrom(variant.getStart(),
                   variant.getEnd());
 
+          /*
+           * only take features whose range is fully mappable to sequence positions
+           */
           if (featureRange != null)
           {
             int featureStart = Math.min(featureRange[0], featureRange[1]);
             int featureEnd = Math.max(featureRange[0], featureRange[1]);
-            count += addAlleleFeatures(seq, variant, featureStart,
-                    featureEnd, forwardStrand);
+            if (featureEnd - featureStart == variant.getEnd()
+                    - variant.getStart())
+            {
+              count += addAlleleFeatures(seq, variant, featureStart,
+                      featureEnd, forwardStrand);
+            }
           }
         }
         variants.close();
@@ -1028,7 +1035,7 @@ public class VCFLoader
      */
     String consequence = getConsequenceForAlleleAndFeature(variant, CSQ_FIELD,
             altAlleleIndex, csqAlleleFieldIndex,
-            csqAlleleNumberFieldIndex, seq.getName().toLowerCase(),
+            csqAlleleNumberFieldIndex, seq.getName().toLowerCase(Locale.ROOT),
             csqFeatureFieldIndex);
 
     /*
@@ -1221,7 +1228,7 @@ public class VCFLoader
       {
         String featureIdentifier = csqFields[featureFieldIndex];
         if (featureIdentifier.length() > 4
-                && seqName.indexOf(featureIdentifier.toLowerCase()) > -1)
+                && seqName.indexOf(featureIdentifier.toLowerCase(Locale.ROOT)) > -1)
         {
           /*
            * feature (transcript) matched - now check for allele match
index 5691a47..582f2de 100644 (file)
@@ -1,5 +1,7 @@
 package jalview.javascript.log4j;
 
+import java.util.Locale;
+
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
@@ -162,7 +164,7 @@ public class Level extends Priority implements Serializable
     {
       return defaultLevel;
     }
-    String s = sArg.toUpperCase();
+    String s = sArg.toUpperCase(Locale.ROOT);
 
     if (s.equals(ALL_NAME))
     {
diff --git a/src/jalview/jbgui/FilterOption.java b/src/jalview/jbgui/FilterOption.java
new file mode 100644 (file)
index 0000000..57a1a42
--- /dev/null
@@ -0,0 +1,119 @@
+package jalview.jbgui;
+
+import jalview.gui.structurechooser.StructureChooserQuerySource;
+import jalview.gui.structurechooser.ThreeDBStructureChooserQuerySource;
+
+/**
+ * This inner class provides the data model for the structure filter combo-box
+ * 
+ * @author tcnofoegbu
+ *
+ */
+public class FilterOption
+{
+  private String name;
+
+  private String value;
+
+  private String view;
+
+  private boolean addSeparatorAfter;
+
+  private StructureChooserQuerySource querySource;
+
+  /**
+   * Model for structure filter option
+   * 
+   * @param name
+   *          - the name of the Option
+   * @param value
+   *          - the value of the option
+   * @param view
+   *          - the category of the filter option
+   * @param addSeparatorAfter
+   *          - if true, a horizontal separator is rendered immediately after
+   *          this filter option, otherwise
+   * @param structureChooserQuerySource
+   *          - the query source that actions this filter
+   */
+  public FilterOption(String name, String value, String view,
+          boolean addSeparatorAfter,
+          StructureChooserQuerySource structureChooserQuerySource)
+  {
+    this.name = name;
+    this.value = value;
+    this.view = view;
+    this.querySource = structureChooserQuerySource;
+    this.addSeparatorAfter = addSeparatorAfter;
+  }
+
+  public String getName()
+  {
+    return name;
+  }
+
+  public void setName(String name)
+  {
+    this.name = name;
+  }
+
+  public String getValue()
+  {
+    return value;
+  }
+
+  public void setValue(String value)
+  {
+    this.value = value;
+  }
+
+  public String getView()
+  {
+    return view;
+  }
+
+  public void setView(String view)
+  {
+    this.view = view;
+  }
+
+  @Override
+  public String toString()
+  {
+    return this.name;
+  }
+
+  public boolean isAddSeparatorAfter()
+  {
+    return addSeparatorAfter;
+  }
+
+  public void setAddSeparatorAfter(boolean addSeparatorAfter)
+  {
+    this.addSeparatorAfter = addSeparatorAfter;
+  }
+
+  public StructureChooserQuerySource getQuerySource()
+  {
+    return querySource;
+  }
+
+  @Override
+  public boolean equals(Object obj)
+  {
+    if (obj instanceof FilterOption) {
+      FilterOption o=(FilterOption) obj;
+      return o.name.equals(name) && o.querySource==querySource && o.value.equals(value) && o.view==view;
+    } else {
+      return super.equals(obj);
+    }
+  }
+
+  @Override
+  public int hashCode()
+  {
+    return ("" + name + ":" + value).hashCode()
+            + (view != null ? view.hashCode() : 0)
+            + (querySource != null ? querySource.hashCode() : 0);
+  }
+}
\ No newline at end of file
index 5af94e4..e6efed6 100755 (executable)
@@ -510,9 +510,12 @@ public class GPreferences extends JPanel
     }
   }
 
-  public final static int CONNECTIONS_TAB = 5;
+  public static enum TabRef
+  {
+    CONNECTIONS_TAB, STRUCTURE_TAB
+  };
 
-  public void selectTab(int selectTab)
+  public void selectTab(TabRef selectTab)
   {
     // select a given tab - currently only for Connections
     switch (selectTab)
@@ -520,6 +523,9 @@ public class GPreferences extends JPanel
     case CONNECTIONS_TAB:
       tabbedPane.setSelectedComponent(connectTab);
       break;
+    case STRUCTURE_TAB:
+      tabbedPane.setSelectedComponent(structureTab);
+      break;
     default:
     }
   }
@@ -1789,8 +1795,8 @@ public class GPreferences extends JPanel
   }
 
   /**
-   * Show a dialog for the user to choose a file. Returns the chosen path, or
-   * null on Cancel.
+   * Show a dialog for the user to choose a file. Returns the chosen path, or null
+   * on Cancel.
    * 
    * @return
    */
@@ -1799,6 +1805,14 @@ public class GPreferences extends JPanel
     String choice = null;
     JFileChooser chooser = new JFileChooser();
 
+    // Enable appBundleIsTraversable in macOS FileChooser to allow selecting
+    // hidden executables within .app dirs
+    if (Platform.isMac())
+    {
+      chooser.putClientProperty("JFileChooser.appBundleIsTraversable",
+              true);
+    }
+
     // chooser.setFileView(new JalviewFileView());
     chooser.setDialogTitle(
             MessageManager.getString("label.open_local_file"));
@@ -3158,7 +3172,7 @@ public class GPreferences extends JPanel
    * DOCUMENT ME!
    * 
    * @param e
-   *          DOCUMENT ME!
+   *            DOCUMENT ME!
    */
   public void ok_actionPerformed(ActionEvent e)
   {
@@ -3168,7 +3182,7 @@ public class GPreferences extends JPanel
    * DOCUMENT ME!
    * 
    * @param e
-   *          DOCUMENT ME!
+   *            DOCUMENT ME!
    */
   public void cancel_actionPerformed(ActionEvent e)
   {
@@ -3178,7 +3192,7 @@ public class GPreferences extends JPanel
    * DOCUMENT ME!
    * 
    * @param e
-   *          DOCUMENT ME!
+   *            DOCUMENT ME!
    */
   public void annotations_actionPerformed(ActionEvent e)
   {
index b9c9267..70d99c1 100644 (file)
@@ -39,6 +39,7 @@ import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.Font;
 import java.awt.GridLayout;
+import java.awt.Point;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
@@ -67,6 +68,7 @@ import javax.swing.JTable;
 import javax.swing.JTextField;
 import javax.swing.ListCellRenderer;
 import javax.swing.Timer;
+import javax.swing.UIManager;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import javax.swing.event.DocumentEvent;
@@ -88,7 +90,7 @@ public abstract class GStructureChooser extends JPanel
 {
   private static final Font VERDANA_12 = new Font("Verdana", 0, 12);
 
-  protected static final String VIEWS_FILTER = "VIEWS_FILTER";
+  public static final String VIEWS_FILTER = "VIEWS_FILTER";
 
   protected static final String VIEWS_FROM_FILE = "VIEWS_FROM_FILE";
 
@@ -119,6 +121,8 @@ public abstract class GStructureChooser extends JPanel
   protected JButton btn_newView;
 
   protected JButton btn_pdbFromFile = new JButton();
+  
+  protected JButton btn_queryTDB = new JButton();
 
   protected JCheckBox chk_superpose = new JCheckBox(
           MessageManager.getString("label.superpose_structures"));
@@ -145,6 +149,9 @@ public abstract class GStructureChooser extends JPanel
   protected ImageIcon warningImage = new ImageIcon(
           getClass().getResource("/images/warning.gif"));
 
+  protected ImageIcon tdbImage = new ImageIcon(
+          getClass().getResource("/images/3d-beacons-logo-transparent.png"));
+
   protected JLabel lbl_loading = new JLabel(loadingImage);
 
   protected JLabel lbl_pdbManualFetchStatus = new JLabel(errorImage);
@@ -161,9 +168,9 @@ public abstract class GStructureChooser extends JPanel
 
   protected JTabbedPane pnl_filter = new JTabbedPane();
 
-  protected FTSDataColumnPreferences pdbDocFieldPrefs = new FTSDataColumnPreferences(
-          PreferenceSource.STRUCTURE_CHOOSER,
-          PDBFTSRestClient.getInstance());
+  protected abstract FTSDataColumnPreferences getFTSDocFieldPrefs();
+  protected abstract void setFTSDocFieldPrefs(FTSDataColumnPreferences newPrefs);
+
 
   protected FTSDataColumnI[] previousWantedFields;
 
@@ -244,11 +251,15 @@ public abstract class GStructureChooser extends JPanel
                                       + "...\"")
                       : JvSwingUtils.wrapTooltip(true, toolTipText)));
       return toolTipText;
-    }
+    }    
   };
 
   public GStructureChooser()
   {
+  }
+  protected void initDialog()
+  {
+
     try
     {
       jbInit();
@@ -261,7 +272,7 @@ public abstract class GStructureChooser extends JPanel
       e.printStackTrace();
     }
   }
-
+  
   // BH SwingJS optimization
   // (a) 100-ms interruptable timer for text entry -- BH 1/10/2019
   // (b) two-character minimum, at least for JavaScript.
@@ -302,15 +313,42 @@ public abstract class GStructureChooser extends JPanel
     tbl_summary.addMouseListener(new MouseAdapter()
     {
       @Override
+      public void mousePressed(MouseEvent e)
+      {
+        if (!popupAction(e))
+        {
+          super.mousePressed(e);
+        }
+      }
+      @Override
       public void mouseClicked(MouseEvent e)
       {
-        validateSelections();
+        if (!popupAction(e))
+        {
+          validateSelections();
+        }
       }
 
       @Override
       public void mouseReleased(MouseEvent e)
       {
+        if (!popupAction(e))
+        {
         validateSelections();
+        }
+      }
+      boolean popupAction(MouseEvent e)
+      {
+       if (e.isPopupTrigger())
+       {
+         Point pt = e.getPoint();
+         int selectedRow = tbl_summary.rowAtPoint(pt);
+         if (showPopupFor(selectedRow,pt.x,pt.y))
+         {
+           return true;
+         }
+       }
+       return false;
       }
     });
     tbl_summary.addKeyListener(new KeyAdapter()
@@ -427,7 +465,9 @@ public abstract class GStructureChooser extends JPanel
       }
     });
 
-    btn_newView = new JButton(MessageManager.getString("action.new_view"));
+    btn_newView = new JButton(
+            MessageManager.formatMessage("action.new_structure_view_with",
+                    StructureViewer.getViewerType().toString()));
     btn_newView.setFont(VERDANA_12);
     btn_newView.addActionListener(new java.awt.event.ActionListener()
     {
@@ -448,6 +488,8 @@ public abstract class GStructureChooser extends JPanel
         }
       }
     });
+    
+    // TODO: JAL-3898 - get list of available external programs to view structures with
 
     btn_add = new JButton(MessageManager.getString("action.add"));
     btn_add.setFont(VERDANA_12);
@@ -543,6 +585,12 @@ public abstract class GStructureChooser extends JPanel
     });
 
     chk_invertFilter.addItemListener(this);
+    btn_queryTDB.setFont(VERDANA_12);
+    //btn_queryTDB.setPreferredSize(new Dimension(200,22));
+    btn_queryTDB.setText(MessageManager.getString("label.search_3dbeacons"));
+    btn_queryTDB.setToolTipText(MessageManager.getString("label.find_models_from_3dbeacons"));
+    btn_queryTDB.setIcon(tdbImage);
+    btn_queryTDB.setVisible(false);
 
     targetView.setVisible(false);
 
@@ -553,10 +601,13 @@ public abstract class GStructureChooser extends JPanel
     actionsPanel.add(btn_newView);
     actionsPanel.add(btn_cancel, "right");
 
-    JPanel pnl_main = new JPanel();
-    pnl_main.add(cmb_filterOption);
-    pnl_main.add(lbl_loading);
-    pnl_main.add(chk_invertFilter);
+    JPanel pnl_main = new JPanel(new BorderLayout());
+    JPanel pnl_controls = new JPanel();
+    pnl_main.add(btn_queryTDB,BorderLayout.NORTH);
+    pnl_controls.add(cmb_filterOption);
+    pnl_controls.add(lbl_loading);
+    pnl_controls.add(chk_invertFilter);
+    pnl_main.add(pnl_controls,BorderLayout.CENTER);
     lbl_loading.setVisible(false);
 
     JPanel pnl_fileChooser = new JPanel(new FlowLayout());
@@ -596,7 +647,7 @@ public abstract class GStructureChooser extends JPanel
           btn_add.setVisible(false);
           btn_newView.setEnabled(false);
           btn_cancel.setVisible(false);
-          previousWantedFields = pdbDocFieldPrefs
+          previousWantedFields = getFTSDocFieldPrefs()
                   .getStructureSummaryFields()
                   .toArray(new FTSDataColumnI[0]);
         }
@@ -618,7 +669,7 @@ public abstract class GStructureChooser extends JPanel
     pnl_filter.addChangeListener(changeListener);
     pnl_filter.setPreferredSize(new Dimension(width, height));
     pnl_filter.add(foundStructureSummary, scrl_foundStructures);
-    pnl_filter.add(configureCols, pdbDocFieldPrefs);
+    pnl_filter.add(configureCols, getFTSDocFieldPrefs());
 
     JPanel pnl_locPDB = new JPanel(new BorderLayout());
     pnl_locPDB.add(scrl_localPDB);
@@ -662,6 +713,7 @@ public abstract class GStructureChooser extends JPanel
   }
 
 
+protected abstract boolean showPopupFor(int selectedRow, int x, int y);
 protected void closeAction(int preferredHeight)
   {
     // System.out.println(">>>>>>>>>> closing internal frame!!!");
@@ -683,7 +735,7 @@ protected void closeAction(int preferredHeight)
       return true;
     }
 
-    FTSDataColumnI[] currentWantedFields = pdbDocFieldPrefs
+    FTSDataColumnI[] currentWantedFields = getFTSDocFieldPrefs()
             .getStructureSummaryFields().toArray(new FTSDataColumnI[0]);
     return Arrays.equals(currentWantedFields, previousWantedFields) ? false
             : true;
@@ -700,91 +752,6 @@ protected void closeAction(int preferredHeight)
   }
 
   /**
-   * This inner class provides the data model for the structure filter combo-box
-   * 
-   * @author tcnofoegbu
-   *
-   */
-  public class FilterOption
-  {
-    private String name;
-
-    private String value;
-
-    private String view;
-
-    private boolean addSeparatorAfter;
-
-    /**
-     * Model for structure filter option
-     * 
-     * @param name
-     *          - the name of the Option
-     * @param value
-     *          - the value of the option
-     * @param view
-     *          - the category of the filter option
-     * @param addSeparatorAfter
-     *          - if true, a horizontal separator is rendered immediately after
-     *          this filter option, otherwise
-     */
-    public FilterOption(String name, String value, String view,
-            boolean addSeparatorAfter)
-    {
-      this.name = name;
-      this.value = value;
-      this.view = view;
-      this.addSeparatorAfter = addSeparatorAfter;
-    }
-
-    public String getName()
-    {
-      return name;
-    }
-
-    public void setName(String name)
-    {
-      this.name = name;
-    }
-
-    public String getValue()
-    {
-      return value;
-    }
-
-    public void setValue(String value)
-    {
-      this.value = value;
-    }
-
-    public String getView()
-    {
-      return view;
-    }
-
-    public void setView(String view)
-    {
-      this.view = view;
-    }
-
-    @Override
-    public String toString()
-    {
-      return this.name;
-    }
-
-    public boolean isAddSeparatorAfter()
-    {
-      return addSeparatorAfter;
-    }
-
-    public void setAddSeparatorAfter(boolean addSeparatorAfter)
-    {
-      this.addSeparatorAfter = addSeparatorAfter;
-    }
-  }
-
-  /**
    * This inner class provides the provides the data model for associate
    * sequence combo-box - cmb_assSeq
    * 
index b3422ab..6b877bf 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.json.binding.biojson.v1;
 
+import java.util.Locale;
+
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.schemes.Blosum62ColourScheme;
 import jalview.schemes.BuriedColourScheme;
@@ -62,7 +64,7 @@ public class ColourSchemeMapper
   public static ColourSchemeI getJalviewColourScheme(
           String colourSchemeName, AnnotatedCollectionI annotCol)
   {
-    switch (colourSchemeName.toUpperCase())
+    switch (colourSchemeName.toUpperCase(Locale.ROOT))
     {
     case "ZAPPO":
       return csZappo;
index 64b3a9c..9b6741b 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.project;
 
+import java.util.Locale;
+
 import static jalview.math.RotatableMatrix.Axis.X;
 import static jalview.math.RotatableMatrix.Axis.Y;
 import static jalview.math.RotatableMatrix.Axis.Z;
@@ -2076,8 +2078,8 @@ public class Jalview2XML
       final PDBEntry pdbentry = bindingModel.getPdbEntry(peid);
       final String pdbId = pdbentry.getId();
       if (!pdbId.equals(entry.getId())
-              && !(entry.getId().length() > 4 && entry.getId().toLowerCase()
-                      .startsWith(pdbId.toLowerCase())))
+              && !(entry.getId().length() > 4 && entry.getId().toLowerCase(Locale.ROOT)
+                      .startsWith(pdbId.toLowerCase(Locale.ROOT))))
       {
         /*
          * not interested in a binding to a different PDB entry here
@@ -2529,6 +2531,7 @@ public class Jalview2XML
         dbref.setSource(ref.getSource());
         dbref.setVersion(ref.getVersion());
         dbref.setAccessionId(ref.getAccessionId());
+        dbref.setCanonical(ref.isCanonical());
         if (ref instanceof GeneLocus)
         {
           dbref.setLocus(true);
@@ -2644,7 +2647,7 @@ public class Jalview2XML
         for (int i = 0; i < colours.length; i++)
         {
           Colour col = new Colour();
-          col.setName(ResidueProperties.aa[i].toLowerCase());
+          col.setName(ResidueProperties.aa[i].toLowerCase(Locale.ROOT));
           col.setRGB(jalview.util.Format.getHexString(colours[i]));
           // jbucs.addColour(col);
           jbucs.getColour().add(col);
@@ -5622,6 +5625,7 @@ public class Jalview2XML
       {
         entry.setMap(addMapping(dr.getMapping()));
       }
+      entry.setCanonical(dr.isCanonical());
       datasetSequence.addDBRef(entry);
     }
   }
index fa2900d..deb2b6f 100644 (file)
@@ -1516,6 +1516,8 @@ public class AnnotationRenderer
                       .deriveFont(AffineTransform.getScaleInstance(sx, sy));
               g.setFont(font);
               g.drawChars(dc, 0, dc.length, x * charWidth, hght);
+              g.setFont(ofont);
+
               ht += newHeight;
             }
           }
index 19723ca..5a2d5dc 100755 (executable)
  */
 package jalview.schemes;
 
+import java.awt.Color;
+import java.util.List;
+import java.util.Map;
+
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceCollectionI;
 import jalview.datamodel.SequenceI;
 import jalview.util.Comparison;
 
-import java.awt.Color;
-import java.util.List;
-import java.util.Map;
-
 public class ClustalxColourScheme extends ResidueColourScheme
 {
   private static final int EIGHTY_FIVE = 85;
@@ -95,7 +95,7 @@ public class ClustalxColourScheme extends ResidueColourScheme
   }
 
   @Override
-  public void alignmentChanged(AnnotatedCollectionI alignment,
+  public synchronized void alignmentChanged(AnnotatedCollectionI alignment,
           Map<SequenceI, SequenceCollectionI> hiddenReps)
   {
     int maxWidth = alignment.getWidth();
@@ -278,7 +278,7 @@ public class ClustalxColourScheme extends ResidueColourScheme
   }
 
   @Override
-  protected Color findColour(char c, int j, SequenceI seq)
+  protected synchronized Color findColour(char c, int j, SequenceI seq)
   {
     // TODO why the test for includeGaps here?
     if (cons2.length <= j || Comparison.isGap(c)
index 141ef10..061ccd4 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.schemes;
 
+import java.util.Locale;
+
 import jalview.xml.binding.jalview.JalviewUserColours;
 
 import java.awt.Color;
@@ -92,7 +94,7 @@ public class ColourSchemeLoader
 
         Color color = new Color(
                 Integer.parseInt(jucs.getColour().get(i).getRGB(), 16));
-        if (name.toLowerCase().equals(name))
+        if (name.toLowerCase(Locale.ROOT).equals(name))
         {
           caseSensitive = true;
           lowerCase[index] = color;
index d31fbba..faada0e 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.schemes;
 
+import java.util.Locale;
+
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.datamodel.SequenceCollectionI;
@@ -104,7 +106,7 @@ public class ColourSchemes
      * name is lower-case for non-case-sensitive lookup
      * (name in the colour keeps its true case)
      */
-    String lower = name.toLowerCase();
+    String lower = name.toLowerCase(Locale.ROOT);
     if (schemes.containsKey(lower))
     {
       System.err
@@ -122,7 +124,7 @@ public class ColourSchemes
   {
     if (name != null)
     {
-      schemes.remove(name.toLowerCase());
+      schemes.remove(name.toLowerCase(Locale.ROOT));
     }
   }
 
@@ -148,7 +150,7 @@ public class ColourSchemes
     {
       return null;
     }
-    ColourSchemeI cs = schemes.get(name.toLowerCase());
+    ColourSchemeI cs = schemes.get(name.toLowerCase(Locale.ROOT));
     return cs == null ? null
             : cs.getInstance(viewport, forData);
   }
@@ -193,6 +195,6 @@ public class ColourSchemes
     {
       return false;
     }
-    return schemes.containsKey(name.toLowerCase());
+    return schemes.containsKey(name.toLowerCase(Locale.ROOT));
   }
 }
index 0d36f4f..e5bda58 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.schemes;
 
+import java.util.Locale;
+
 import jalview.api.FeatureColourI;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.features.FeatureMatcher;
@@ -194,19 +196,19 @@ public class FeatureColour implements FeatureColourI
               "Expected either 'label' or a colour specification in the line: "
                       + descriptor);
     }
-    if (nextToken.toLowerCase().startsWith(LABEL))
+    if (nextToken.toLowerCase(Locale.ROOT).startsWith(LABEL))
     {
       byLabel = true;
       // get the token after the next delimiter:
       mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
       mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
     }
-    else if (nextToken.toLowerCase().startsWith(SCORE))
+    else if (nextToken.toLowerCase(Locale.ROOT).startsWith(SCORE))
     {
       mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
       mincol = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
     }
-    else if (nextToken.toLowerCase().startsWith(ATTRIBUTE))
+    else if (nextToken.toLowerCase(Locale.ROOT).startsWith(ATTRIBUTE))
     {
       byAttribute = true;
       attName = (gcol.hasMoreTokens() ? gcol.nextToken() : null);
@@ -304,7 +306,7 @@ public class FeatureColour implements FeatureColourI
       }
 
       gcol.nextToken(); // skip next '|'
-      if (tok.toLowerCase().startsWith(ABSOLUTE))
+      if (tok.toLowerCase(Locale.ROOT).startsWith(ABSOLUTE))
       {
         minval = gcol.nextToken();
         gcol.nextToken(); // skip next '|'
@@ -380,17 +382,17 @@ public class FeatureColour implements FeatureColourI
       {
         // threshold type and possibly a threshold value
         ttype = gcol.nextToken();
-        if (ttype.toLowerCase().startsWith(BELOW))
+        if (ttype.toLowerCase(Locale.ROOT).startsWith(BELOW))
         {
           featureColour.setBelowThreshold(true);
         }
-        else if (ttype.toLowerCase().startsWith(ABOVE))
+        else if (ttype.toLowerCase(Locale.ROOT).startsWith(ABOVE))
         {
           featureColour.setAboveThreshold(true);
         }
         else
         {
-          if (!ttype.toLowerCase().startsWith("no"))
+          if (!ttype.toLowerCase(Locale.ROOT).startsWith("no"))
           {
             System.err.println(
                     "Ignoring unrecognised threshold type : " + ttype);
index b15e4cf..6e8554f 100644 (file)
@@ -36,6 +36,12 @@ public class FeatureSettingsAdapter implements FeatureSettingsModelI
   }
 
   @Override
+  public boolean isFeatureHidden(String type)
+  {
+    return false;
+  }
+
+  @Override
   public boolean isGroupDisplayed(String group)
   {
     return true;
index 5f84ca0..2eb303d 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.schemes;
 
+import java.util.Locale;
+
 import jalview.analysis.GeneticCodes;
 
 import java.awt.Color;
@@ -2347,7 +2349,7 @@ public class ResidueProperties
         {
           continue;
         }
-        nuc = nuc.toUpperCase();
+        nuc = nuc.toUpperCase(Locale.ROOT);
         if (!result.contains(nuc))
         {
           result.add(nuc);
@@ -2366,7 +2368,7 @@ public class ResidueProperties
         {
           continue;
         }
-        res = res.toUpperCase();
+        res = res.toUpperCase(Locale.ROOT);
         if (!result.contains(res))
         {
           result.add(res);
@@ -2391,7 +2393,7 @@ public class ResidueProperties
       return '0';
     }
     Integer index = ResidueProperties.aa3Hash
-            .get(threeLetterCode.toUpperCase());
+            .get(threeLetterCode.toUpperCase(Locale.ROOT));
     return index == null ? '0' : aa[index].charAt(0);
   }
 }
index d77f2f5..d55ffbf 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.schemes;
 
+import java.util.Locale;
+
 import jalview.api.AlignViewportI;
 import jalview.datamodel.AnnotatedCollectionI;
 import jalview.util.ColorUtils;
@@ -215,7 +217,7 @@ public class UserColourScheme extends ResidueColourScheme
             continue;
           }
 
-          if (residue.equals(residue.toLowerCase()))
+          if (residue.equals(residue.toLowerCase(Locale.ROOT)))
           {
             if (lowerCaseColours == null)
             {
@@ -306,7 +308,7 @@ public class UserColourScheme extends ResidueColourScheme
         c = lowerCaseColours[index];
         if (c != null && !c.equals(Color.white))
         {
-          residue = residue.toLowerCase();
+          residue = residue.toLowerCase(Locale.ROOT);
           if (colours.get(c) == null)
           {
             colours.put(c, new ArrayList<String>());
index 9662fee..cbd59b1 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.structure;
 
+import java.util.Locale;
+
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.PDBEntry.Type;
 
@@ -130,7 +132,7 @@ public class StructureImportSettings
           String defaultStructureFileFormat)
   {
     StructureImportSettings.defaultStructureFileFormat = PDBEntry.Type
-            .valueOf(defaultStructureFileFormat.toUpperCase());
+            .valueOf(defaultStructureFileFormat.toUpperCase(Locale.ROOT));
   }
 
   public static String getDefaultPDBFileParser()
@@ -147,7 +149,7 @@ public class StructureImportSettings
   public static void setDefaultPDBFileParser(String defaultPDBFileParser)
   {
     StructureImportSettings.defaultPDBFileParser = StructureParser
-            .valueOf(defaultPDBFileParser.toUpperCase());
+            .valueOf(defaultPDBFileParser.toUpperCase(Locale.ROOT));
   }
 
 }
index 53644e9..1fcbbf5 100644 (file)
@@ -410,7 +410,10 @@ public class StructureSelectionManager
         registerPDBFile(pdb.getId().trim(), pdbFile);
       }
       // if PDBId is unavailable then skip SIFTS mapping execution path
-      isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable();
+      // TODO: JAL-3868 need to know if structure is actually from 
+      // PDB (has valid PDB ID and has provenance suggesting it 
+      // actually came from PDB)
+      isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable() && !pdb.getId().startsWith("AF-");
 
     } catch (Exception ex)
     {
index 16dd96c..748381f 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.structures.models;
 
+import java.util.Locale;
+
 import java.awt.Color;
 import java.io.File;
 import java.io.IOException;
@@ -67,7 +69,7 @@ import jalview.util.MessageManager;
 
 /**
  * 
- * A base class to hold common function for protein structure model binding.
+ * A base class to hold common function for 3D structure model binding.
  * Initial version created by refactoring JMol and Chimera binding models, but
  * other structure viewers could in principle be accommodated in future.
  * 
@@ -582,6 +584,7 @@ public abstract class AAStructureBindingModel
       }
     }
   }
+  
 
   @Override
   public abstract void highlightAtoms(List<AtomSpec> atoms);
@@ -930,7 +933,7 @@ public abstract class AAStructureBindingModel
           for (String reply : replies)
           {
             // return this error (Chimera only) to the user
-            if (reply.toLowerCase().contains("unequal numbers of atoms"))
+            if (reply.toLowerCase(Locale.ROOT).contains("unequal numbers of atoms"))
             {
               error += "; " + reply;
             }
@@ -1096,7 +1099,10 @@ public abstract class AAStructureBindingModel
         for (StructureCommandI cmd : cmds)
         {
           List<String> replies = executeCommand(cmd, true);
-          response.addAll(replies);
+          if (replies != null)
+          {
+            response.addAll(replies);
+          }
         }
         return response;
       } finally
@@ -1213,8 +1219,7 @@ public abstract class AAStructureBindingModel
   }
 
   /**
-   * Returns the FeatureRenderer for the given alignment view, or null if
-   * feature display is turned off in the view.
+   * Returns the FeatureRenderer for the given alignment view
    * 
    * @param avp
    * @return
@@ -1227,9 +1232,7 @@ public abstract class AAStructureBindingModel
     {
       return null;
     }
-    return ap.getAlignViewport().isShowSequenceFeatures()
-            ? ap.getFeatureRenderer()
-            : null;
+    return ap.getFeatureRenderer();
   }
 
   protected void setStructureCommands(StructureCommandsI cmd)
index a01c035..536671d 100644 (file)
@@ -25,6 +25,8 @@ package jalview.util;
  * want to preserve case, but do not want to duplicate upper and lower case
  * variants
  */
+import java.util.Locale;
+
 public final class CaseInsensitiveString
 {
   String value;
@@ -72,6 +74,6 @@ public final class CaseInsensitiveString
   public int hashCode()
   {
     return value == null ? super.hashCode()
-            : value.toUpperCase().hashCode();
+            : value.toUpperCase(Locale.ROOT).hashCode();
   }
 }
index ede528f..109eaa5 100644 (file)
@@ -55,6 +55,7 @@ public class ChannelProperties
     defaultProps.put("uod_banner.32", "/default_images/UoD_banner-32.png");
     defaultProps.put("default_appbase",
             "https://www.jalview.org/getdown/release/1.8");
+    defaultProps.put("preferences.filename", ".jalview_properties");
 
     // load channel_properties
     Properties tryChannelProps = new Properties();
@@ -63,12 +64,11 @@ public class ChannelProperties
     if (channelPropsURL == null)
     {
       // complete failure of channel_properties, set all properties to defaults
-      System.err
-              .println("Failed to find '" + CHANNEL_PROPERTIES_FILENAME
-                      + "' file at '"
-                      + (channelPropsURL == null ? "null"
-                              : channelPropsURL.toString())
-                      + "'. Using class defaultProps.");
+      System.err.println("Failed to find '" + CHANNEL_PROPERTIES_FILENAME
+              + "' file at '"
+              + (channelPropsURL == null ? "null"
+                      : channelPropsURL.toString())
+              + "'. Using class defaultProps.");
       tryChannelProps = defaultProps;
     }
     else
index 60129fb..5db333f 100644 (file)
@@ -24,6 +24,8 @@
 
 package jalview.util;
 
+import java.util.Locale;
+
 import java.awt.Color;
 import java.util.HashMap;
 import java.util.Map;
@@ -324,7 +326,7 @@ public class ColorUtils
       return null;
     }
     Color col = null;
-    name = name.toLowerCase();
+    name = name.toLowerCase(Locale.ROOT);
 
     // or make a static map; or use reflection on the field name
     switch (name)
index 286bfb2..0d945ac 100644 (file)
@@ -261,7 +261,7 @@ public class Comparison
 
   /**
    * Overloaded method signature to test whether a single sequence is nucleotide
-   * (that is, more than 85% CGTA)
+   * (that is, more than 85% CGTAUNX)
    * 
    * @param seq
    * @return
@@ -274,27 +274,32 @@ public class Comparison
     }
     long ntCount = 0;
     long aaCount = 0;
+    long nCount = 0;
 
     int len = seq.getLength();
     for (int i = 0; i < len; i++)
     {
       char c = seq.getCharAt(i);
-      if (isNucleotide(c))
+      if (isNucleotide(c) || isX(c))
       {
         ntCount++;
       }
       else if (!isGap(c))
       {
         aaCount++;
+        if (isN(c))
+        {
+          nCount++;
+        }
       }
     }
     /*
      * Check for nucleotide count > 85% of total count (in a form that evades
      * int / float conversion or divide by zero).
      */
-    if (ntCount * 100 > EIGHTY_FIVE * (ntCount + aaCount))
+    if ((ntCount+nCount) * 100 > EIGHTY_FIVE * (ntCount + aaCount))
     {
-      return true;
+      return ntCount>0; // all N is considered protein. Could use a threshold here too
     }
     else
     {
@@ -317,17 +322,18 @@ public class Comparison
       return false;
     }
     // true if we have seen a nucleotide sequence
-    boolean na=false;
+    boolean na = false;
     for (SequenceI seq : seqs)
     {
       if (seq == null)
       {
         continue;
       }
-      na=true;
+      na = true;
       // TODO could possibly make an informed guess just from the first sequence
       // to save a lengthy calculation
-      if (seq.isProtein()) {
+      if (seq.isProtein())
+      {
         // if even one looks like protein, the alignment is protein
         return false;
       }
@@ -347,7 +353,6 @@ public class Comparison
     {
       c -= TO_UPPER_CASE;
     }
-
     switch (c)
     {
     case 'A':
@@ -360,6 +365,28 @@ public class Comparison
     return false;
   }
 
+  public static boolean isN(char c)
+  {
+    switch (c)
+    {
+    case 'N':
+    case 'n':
+      return true;
+    }
+    return false;
+  }
+
+  public static boolean isX(char c)
+  {
+    switch (c)
+    {
+    case 'X':
+    case 'x':
+      return true;
+    }
+    return false;
+  }
+
   /**
    * Answers true if every character in the string is one of aAcCgGtTuU, or
    * (optionally) a gap character (dot, dash, space), else false
index ae0243e..bcb0bd3 100755 (executable)
@@ -20,6 +20,8 @@
  */
 package jalview.util;
 
+import java.util.Locale;
+
 import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.HashMap;
@@ -73,7 +75,7 @@ public class DBRefUtils
            // guarantee we always have lowercase entries for canonical string lookups
            for (String k : canonicalSourceNameLookup.keySet())
            {
-             canonicalSourceNameLookup.put(k.toLowerCase(),
+             canonicalSourceNameLookup.put(k.toLowerCase(Locale.ROOT),
                      canonicalSourceNameLookup.get(k));
            }
    }
@@ -98,7 +100,7 @@ public class DBRefUtils
        HashSet<String> srcs = new HashSet<String>();
        for (String src : sources) 
        {
-         srcs.add(src.toUpperCase());
+         srcs.add(src.toUpperCase(Locale.ROOT));
        }
 
        int nrefs = dbrefs.size();
@@ -107,7 +109,7 @@ public class DBRefUtils
        {
          DBRefEntry dbr = dbrefs.get(ib);
          String source = getCanonicalName(dbr.getSource());
-         if (srcs.contains(source.toUpperCase())) 
+         if (srcs.contains(source.toUpperCase(Locale.ROOT))) 
          {
            res.add(dbr);
          }
@@ -181,7 +183,7 @@ public class DBRefUtils
          {
                return null;
          }
-         String canonical = canonicalSourceNameLookup.get(source.toLowerCase());
+         String canonical = canonicalSourceNameLookup.get(source.toLowerCase(Locale.ROOT));
          return canonical == null ? source : canonical;
        }
 
index 284ec10..654b03a 100644 (file)
@@ -47,6 +47,7 @@ public class DnaUtils
   public static List<int[]> parseLocation(String location)
           throws ParseException
   {
+    location = location.trim(); // failsafe for untidy input data
     if (location.startsWith("join("))
     {
       return parseJoin(location);
index 74f77a2..5473f5a 100644 (file)
@@ -22,7 +22,12 @@ package jalview.util;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.ProtocolException;
 import java.net.URL;
+import java.util.List;
+
+import javax.ws.rs.HttpMethod;
 
 public class HttpUtils
 {
@@ -68,5 +73,32 @@ public class HttpUtils
   {
     return file.startsWith("http://") || file.startsWith("https://");
   }
+  
+
+  /**
+   * wrapper to get/post to a URL or check headers
+   * @param url
+   * @param ids
+   * @param readTimeout
+   * @return
+   * @throws IOException
+   * @throws ProtocolException
+   */
+  public static boolean checkUrlAvailable(URL url,
+          int readTimeout) throws IOException, ProtocolException
+  {
+    // System.out.println(System.currentTimeMillis() + " " + url);
+
+    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+
+    connection.setRequestMethod(HttpMethod.HEAD);
+
+    connection.setDoInput(true);
+
+    connection.setUseCaches(false);
+    connection.setConnectTimeout(300);
+    connection.setReadTimeout(readTimeout);
+    return connection.getResponseCode() == 200;
+  }
 
 }
index 731e976..3555e52 100644 (file)
@@ -22,6 +22,7 @@ package jalview.util;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.List;
 
 /**
@@ -30,8 +31,6 @@ import java.util.List;
  * 
  * Use at your own risk!
  * 
- * TODO: efficient implementation of private posMap method
- * 
  * TODO: test/ensure that sense of from and to ratio start position is conserved
  * (codon start position recovery)
  */
@@ -209,8 +208,7 @@ public class MapList
 
   /**
    * Constructor given from and to ranges as [start1, end1, start2, end2,...].
-   * If any end is equal to the next start, the ranges will be merged. There is
-   * no validation check that the ranges do not overlap each other.
+   * There is no validation check that the ranges do not overlap each other.
    * 
    * @param from
    *          contiguous regions as [start1, end1, start2, end2, ...]
@@ -228,7 +226,6 @@ public class MapList
     this.toRatio = toRatio;
     fromLowest = Integer.MAX_VALUE;
     fromHighest = Integer.MIN_VALUE;
-    int added = 0;
 
     for (int i = 0; i < from.length; i += 2)
     {
@@ -238,36 +235,16 @@ public class MapList
        */
       fromLowest = Math.min(fromLowest, Math.min(from[i], from[i + 1]));
       fromHighest = Math.max(fromHighest, Math.max(from[i], from[i + 1]));
-      if (added > 0 && from[i] == fromShifts.get(added - 1)[1])
-      {
-        /*
-         * this range starts where the last ended - just extend it
-         */
-        fromShifts.get(added - 1)[1] = from[i + 1];
-      }
-      else
-      {
-        fromShifts.add(new int[] { from[i], from[i + 1] });
-        added++;
-      }
+      fromShifts.add(new int[] { from[i], from[i + 1] });
     }
 
     toLowest = Integer.MAX_VALUE;
     toHighest = Integer.MIN_VALUE;
-    added = 0;
     for (int i = 0; i < to.length; i += 2)
     {
       toLowest = Math.min(toLowest, Math.min(to[i], to[i + 1]));
       toHighest = Math.max(toHighest, Math.max(to[i], to[i + 1]));
-      if (added > 0 && to[i] == toShifts.get(added - 1)[1])
-      {
-        toShifts.get(added - 1)[1] = to[i + 1];
-      }
-      else
-      {
-        toShifts.add(new int[] { to[i], to[i + 1] });
-        added++;
-      }
+      toShifts.add(new int[] { to[i], to[i + 1] });
     }
   }
 
@@ -330,9 +307,8 @@ public class MapList
       if (range.length != 2)
       {
         // throw new IllegalArgumentException(range);
-        System.err.println(
-                "Invalid format for fromRange " + Arrays.toString(range)
-                + " may cause errors");
+        System.err.println("Invalid format for fromRange "
+                + Arrays.toString(range) + " may cause errors");
       }
       fromLowest = Math.min(fromLowest, Math.min(range[0], range[1]));
       fromHighest = Math.max(fromHighest, Math.max(range[0], range[1]));
@@ -346,8 +322,7 @@ public class MapList
       {
         // throw new IllegalArgumentException(range);
         System.err.println("Invalid format for toRange "
-                + Arrays.toString(range)
-                + " may cause errors");
+                + Arrays.toString(range) + " may cause errors");
       }
       toLowest = Math.min(toLowest, Math.min(range[0], range[1]));
       toHighest = Math.max(toHighest, Math.max(range[0], range[1]));
@@ -357,6 +332,17 @@ public class MapList
   /**
    * Consolidates a list of ranges so that any contiguous ranges are merged.
    * This assumes the ranges are already in start order (does not sort them).
+   * <p>
+   * The main use case for this method is when mapping cDNA sequence to its
+   * protein product, based on CDS feature ranges which derive from spliced
+   * exons, but are contiguous on the cDNA sequence. For example
+   * 
+   * <pre>
+   *   CDS 1-20  // from exon1
+   *   CDS 21-35 // from exon2
+   *   CDS 36-71 // from exon3
+   * 'coalesce' to range 1-71
+   * </pre>
    * 
    * @param ranges
    * @return the same list (if unchanged), else a new merged list, leaving the
@@ -384,27 +370,6 @@ public class MapList
         first = false;
         continue;
       }
-      if (range[0] == lastRange[0] && range[1] == lastRange[1])
-      {
-        // drop duplicate range
-        changed = true;
-        continue;
-      }
-
-      /*
-       * drop this range if it lies within the last range
-       */
-      if ((lastDirection == 1 && range[0] >= lastRange[0]
-              && range[0] <= lastRange[1] && range[1] >= lastRange[0]
-              && range[1] <= lastRange[1])
-              || (lastDirection == -1 && range[0] <= lastRange[0]
-                      && range[0] >= lastRange[1]
-                      && range[1] <= lastRange[0]
-                      && range[1] >= lastRange[1]))
-      {
-        changed = true;
-        continue;
-      }
 
       int direction = range[1] >= range[0] ? 1 : -1;
 
@@ -415,11 +380,7 @@ public class MapList
       boolean sameDirection = range[1] == range[0]
               || direction == lastDirection;
       boolean extending = range[0] == lastRange[1] + lastDirection;
-      boolean overlapping = (lastDirection == 1 && range[0] >= lastRange[0]
-              && range[0] <= lastRange[1])
-              || (lastDirection == -1 && range[0] <= lastRange[0]
-                      && range[0] >= lastRange[1]);
-      if (sameDirection && (overlapping || extending))
+      if (sameDirection && extending)
       {
         lastRange[1] = range[1];
         changed = true;
@@ -444,7 +405,7 @@ public class MapList
    */
   protected int[][] makeFromMap()
   {
-    // TODO not used - remove??
+    // TODO only used for test - remove??
     return posMap(fromShifts, fromRatio, toShifts, toRatio);
   }
 
@@ -455,7 +416,7 @@ public class MapList
    */
   protected int[][] makeToMap()
   {
-    // TODO not used - remove??
+    // TODO only used for test - remove??
     return posMap(toShifts, toRatio, fromShifts, fromRatio);
   }
 
@@ -466,10 +427,10 @@ public class MapList
    * @return int[] { from, to pos in range }, int[range.to-range.from+1]
    *         returning mapped position
    */
-  private int[][] posMap(List<int[]> shiftTo, int ratio,
-          List<int[]> shiftFrom, int toRatio)
+  private int[][] posMap(List<int[]> shiftTo, int sourceRatio,
+          List<int[]> shiftFrom, int targetRatio)
   {
-    // TODO not used - remove??
+    // TODO only used for test - remove??
     int iv = 0, ivSize = shiftTo.size();
     if (iv >= ivSize)
     {
@@ -506,7 +467,7 @@ public class MapList
     int mp[][] = new int[to - from + 2][];
     for (int i = 0; i < mp.length; i++)
     {
-      int[] m = shift(i + from, shiftTo, ratio, shiftFrom, toRatio);
+      int[] m = shift(i + from, shiftTo, sourceRatio, shiftFrom, targetRatio);
       if (m != null)
       {
         if (i == 0)
@@ -600,37 +561,51 @@ public class MapList
           List<int[]> shiftFrom, int toRatio)
   {
     // TODO: javadoc; tests
-    int[] fromCount = countPos(shiftTo, pos);
+    int[] fromCount = countPositions(shiftTo, pos);
     if (fromCount == null)
     {
       return null;
     }
     int fromRemainder = (fromCount[0] - 1) % fromRatio;
     int toCount = 1 + (((fromCount[0] - 1) / fromRatio) * toRatio);
-    int[] toPos = countToPos(shiftFrom, toCount);
+    int[] toPos = traverseToPosition(shiftFrom, toCount);
     if (toPos == null)
     {
-      return null; // throw new Error("Bad Mapping!");
+      return null;
     }
-    // System.out.println(fromCount[0]+" "+fromCount[1]+" "+toCount);
     return new int[] { toPos[0], fromRemainder, toPos[1] };
   }
 
   /**
-   * count how many positions pos is along the series of intervals.
+   * Counts how many positions pos is along the series of intervals. Returns an
+   * array of two values:
+   * <ul>
+   * <li>the number of positions traversed (inclusive) to reach {@code pos}</li>
+   * <li>+1 if the last interval traversed is forward, -1 if in a negative
+   * direction</li>
+   * </ul>
+   * Returns null if {@code pos} does not lie in any of the given intervals.
    * 
-   * @param shiftTo
+   * @param intervals
+   *          a list of start-end intervals
    * @param pos
-   * @return number of positions or null if pos is not within intervals
+   *          a position that may lie in one (or more) of the intervals
+   * @return
    */
-  protected static int[] countPos(List<int[]> shiftTo, int pos)
+  protected static int[] countPositions(List<int[]> intervals, int pos)
   {
-    int count = 0, intv[], iv = 0, ivSize = shiftTo.size();
+    int count = 0;
+    int iv = 0;
+    int ivSize = intervals.size();
+
     while (iv < ivSize)
     {
-      intv = shiftTo.get(iv++);
+      int[] intv = intervals.get(iv++);
       if (intv[0] <= intv[1])
       {
+        /*
+         * forwards interval
+         */
         if (pos >= intv[0] && pos <= intv[1])
         {
           return new int[] { count + pos - intv[0] + 1, +1 };
@@ -642,6 +617,9 @@ public class MapList
       }
       else
       {
+        /*
+         * reverse interval
+         */
         if (pos >= intv[1] && pos <= intv[0])
         {
           return new int[] { count + intv[0] - pos + 1, -1 };
@@ -656,79 +634,61 @@ public class MapList
   }
 
   /**
-   * count out pos positions into a series of intervals and return the position
+   * Reads through the given intervals until {@code count} positions have been
+   * traversed, and returns an array consisting of two values:
+   * <ul>
+   * <li>the value at the {@code count'th} position</li>
+   * <li>+1 if the last interval read is forwards, -1 if reverse direction</li>
+   * </ul>
+   * Returns null if the ranges include less than {@code count} positions, or if
+   * {@code count < 1}.
    * 
-   * @param shiftFrom
-   * @param pos
-   * @return position pos in interval set
+   * @param intervals
+   *          a list of [start, end] ranges
+   * @param count
+   *          the number of positions to traverse
+   * @return
    */
-  protected static int[] countToPos(List<int[]> shiftFrom, int pos)
+  protected static int[] traverseToPosition(List<int[]> intervals,
+          final int count)
   {
-    int count = 0, diff = 0, iv = 0, ivSize = shiftFrom.size();
-    int[] intv = { 0, 0 };
+    int traversed = 0;
+    int ivSize = intervals.size();
+    int iv = 0;
+
+    if (count < 1)
+    {
+      return null;
+    }
+
     while (iv < ivSize)
     {
-      intv = shiftFrom.get(iv++);
-      diff = intv[1] - intv[0];
+      int[] intv = intervals.get(iv++);
+      int diff = intv[1] - intv[0];
       if (diff >= 0)
       {
-        if (pos <= count + 1 + diff)
+        if (count <= traversed + 1 + diff)
         {
-          return new int[] { pos - count - 1 + intv[0], +1 };
+          return new int[] { intv[0] + (count - traversed - 1), +1 };
         }
         else
         {
-          count += 1 + diff;
+          traversed += 1 + diff;
         }
       }
       else
       {
-        if (pos <= count + 1 - diff)
+        if (count <= traversed + 1 - diff)
         {
-          return new int[] { intv[0] - (pos - count - 1), -1 };
+          return new int[] { intv[0] - (count - traversed - 1), -1 };
         }
         else
         {
-          count += 1 - diff;
+          traversed += 1 - diff;
         }
       }
     }
-    return null;// (diff<0) ? (intv[1]-1) : (intv[0]+1);
-  }
-
-  /**
-   * find series of intervals mapping from start-end in the From map.
-   * 
-   * @param start
-   *          position mapped 'to'
-   * @param end
-   *          position mapped 'to'
-   * @return series of [start, end] ranges in sequence mapped 'from'
-   */
-  public int[] locateInFrom(int start, int end)
-  {
-    // inefficient implementation
-    int fromStart[] = shiftTo(start);
-    // needs to be inclusive of end of symbol position
-    int fromEnd[] = shiftTo(end);
-
-    return getIntervals(fromShifts, fromStart, fromEnd, fromRatio);
-  }
-
-  /**
-   * find series of intervals mapping from start-end in the to map.
-   * 
-   * @param start
-   *          position mapped 'from'
-   * @param end
-   *          position mapped 'from'
-   * @return series of [start, end] ranges in sequence mapped 'to'
-   */
-  public int[] locateInTo(int start, int end)
-  {
-    int toStart[] = shiftFrom(start);
-    int toEnd[] = shiftFrom(end);
-    return getIntervals(toShifts, toStart, toEnd, toRatio);
+    return null;
   }
 
   /**
@@ -907,7 +867,6 @@ public class MapList
    */
   public int getToPosition(int mpos)
   {
-    // TODO not used - remove??
     int[] mp = shiftTo(mpos);
     if (mp != null)
     {
@@ -917,53 +876,6 @@ public class MapList
   }
 
   /**
-   * get range of positions in To frame for the mpos word in From
-   * 
-   * @param mpos
-   *          position in From
-   * @return null or int[] first position in To for mpos, last position in to
-   *         for Mpos
-   */
-  public int[] getToWord(int mpos)
-  {
-    int[] mp = shiftTo(mpos);
-    if (mp != null)
-    {
-      return new int[] { mp[0], mp[0] + mp[2] * (getFromRatio() - 1) };
-    }
-    return null;
-  }
-
-  /**
-   * get From position in the associated reference frame for position pos in the
-   * associated sequence.
-   * 
-   * @param pos
-   * @return
-   */
-  public int getMappedPosition(int pos)
-  {
-    // TODO not used - remove??
-    int[] mp = shiftFrom(pos);
-    if (mp != null)
-    {
-      return mp[0];
-    }
-    return pos;
-  }
-
-  public int[] getMappedWord(int pos)
-  {
-    // TODO not used - remove??
-    int[] mp = shiftFrom(pos);
-    if (mp != null)
-    {
-      return new int[] { mp[0], mp[0] + mp[2] * (getToRatio() - 1) };
-    }
-    return null;
-  }
-
-  /**
    * 
    * @return a MapList whose From range is this maplist's To Range, and vice
    *         versa
@@ -975,33 +887,6 @@ public class MapList
   }
 
   /**
-   * test for containment rather than equivalence to another mapping
-   * 
-   * @param map
-   *          to be tested for containment
-   * @return true if local or mapped range map contains or is contained by this
-   *         mapping
-   */
-  public boolean containsEither(boolean local, MapList map)
-  {
-    // TODO not used - remove?
-    if (local)
-    {
-      return ((getFromLowest() >= map.getFromLowest()
-              && getFromHighest() <= map.getFromHighest())
-              || (getFromLowest() <= map.getFromLowest()
-                      && getFromHighest() >= map.getFromHighest()));
-    }
-    else
-    {
-      return ((getToLowest() >= map.getToLowest()
-              && getToHighest() <= map.getToHighest())
-              || (getToLowest() <= map.getToLowest()
-                      && getToHighest() >= map.getToHighest()));
-    }
-  }
-
-  /**
    * String representation - for debugging, not guaranteed not to change
    */
   @Override
@@ -1134,8 +1019,8 @@ public class MapList
   }
 
   /**
-   * A helper method that returns true unless at least one range has start > end.
-   * Behaviour is undefined for a mixture of forward and reverse ranges.
+   * A helper method that returns true unless at least one range has start >
+   * end. Behaviour is undefined for a mixture of forward and reverse ranges.
    * 
    * @param ranges
    * @return
@@ -1216,6 +1101,7 @@ public class MapList
     List<int[]> toRanges = new ArrayList<>();
     for (int[] range : getToRanges())
     {
+      int fromLength = Math.abs(range[1] - range[0]) + 1;
       int[] transferred = map.locateInTo(range[0], range[1]);
       if (transferred == null || transferred.length % 2 != 0)
       {
@@ -1226,11 +1112,21 @@ public class MapList
        *  convert [start1, end1, start2, end2, ...] 
        *  to [[start1, end1], [start2, end2], ...]
        */
+      int toLength = 0;
       for (int i = 0; i < transferred.length;)
       {
         toRanges.add(new int[] { transferred[i], transferred[i + 1] });
+        toLength += Math.abs(transferred[i + 1] - transferred[i]) + 1;
         i += 2;
       }
+
+      /*
+       * check we mapped the full range - if not, abort
+       */
+      if (fromLength * map.getToRatio() != toLength * map.getFromRatio())
+      {
+        return null;
+      }
     }
 
     return new MapList(getFromRanges(), toRanges, outFromRatio, outToRatio);
@@ -1246,4 +1142,281 @@ public class MapList
   {
     return fromShifts.size() == 1 && toShifts.size() == 1;
   }
+
+  /**
+   * Returns the [start1, end1, start2, end2, ...] positions in the 'from' range
+   * that map to positions between {@code start} and {@code end} in the 'to'
+   * range. Note that for a reverse strand mapping this will return ranges with
+   * end < start. Returns null if no mapped positions are found in start-end.
+   * 
+   * @param start
+   * @param end
+   * @return
+   */
+  public int[] locateInFrom(int start, int end)
+  {
+    return mapPositions(start, end, toShifts, fromShifts,
+            toRatio, fromRatio);
+  }
+
+  /**
+   * Returns the [start1, end1, start2, end2, ...] positions in the 'to' range
+   * that map to positions between {@code start} and {@code end} in the 'from'
+   * range. Note that for a reverse strand mapping this will return ranges with
+   * end < start. Returns null if no mapped positions are found in start-end.
+   * 
+   * @param start
+   * @param end
+   * @return
+   */
+  public int[] locateInTo(int start, int end)
+  {
+    return mapPositions(start, end, fromShifts, toShifts,
+            fromRatio, toRatio);
+  }
+
+  /**
+   * Helper method that returns the [start1, end1, start2, end2, ...] positions
+   * in {@code targetRange} that map to positions between {@code start} and
+   * {@code end} in {@code sourceRange}. Note that for a reverse strand mapping
+   * this will return ranges with end < start. Returns null if no mapped
+   * positions are found in start-end.
+   * 
+   * @param start
+   * @param end
+   * @param sourceRange
+   * @param targetRange
+   * @param sourceWordLength
+   * @param targetWordLength
+   * @return
+   */
+  final static int[] mapPositions(int start, int end,
+          List<int[]> sourceRange, List<int[]> targetRange,
+          int sourceWordLength, int targetWordLength)
+  {
+    if (end < start)
+    {
+      int tmp = end;
+      end = start;
+      start = tmp;
+    }
+
+    /*
+     * traverse sourceRange and mark offsets in targetRange 
+     * of any positions that lie in [start, end]
+     */
+    BitSet offsets = getMappedOffsetsForPositions(start, end, sourceRange,
+            sourceWordLength, targetWordLength);
+
+    /*
+     * traverse targetRange and collect positions at the marked offsets
+     */
+    List<int[]> mapped = getPositionsForOffsets(targetRange, offsets);
+
+    // TODO: or just return the List and adjust calling code to match
+    return mapped.isEmpty() ? null : MappingUtils.rangeListToArray(mapped);
+  }
+
+  /**
+   * Scans the list of {@code ranges} for any values (positions) that lie
+   * between start and end (inclusive), and records the <em>offsets</em> from
+   * the start of the list as a BitSet. The offset positions are converted to
+   * corresponding words in blocks of {@code wordLength2}.
+   * 
+   * <pre>
+   * For example:
+   * 1:1 (e.g. gene to CDS):
+   * ranges { [10-20], [31-40] }, wordLengthFrom = wordLength 2 = 1
+   *   for start = 1, end = 9, returns a BitSet with no bits set
+   *   for start = 1, end = 11, returns a BitSet with bits 0-1 set
+   *   for start = 15, end = 35, returns a BitSet with bits 5-15 set
+   * 1:3 (peptide to codon):
+   * ranges { [1-200] }, wordLengthFrom = 1, wordLength 2 = 3
+   *   for start = 9, end = 9, returns a BitSet with bits 24-26 set
+   * 3:1 (codon to peptide):
+   * ranges { [101-150], [171-180] }, wordLengthFrom = 3, wordLength 2 = 1
+   *   for start = 101, end = 102 (partial first codon), returns a BitSet with bit 0 set
+   *   for start = 150, end = 171 (partial 17th codon), returns a BitSet with bit 16 set
+   * 3:1 (circular DNA to peptide):
+   * ranges { [101-150], [21-30] }, wordLengthFrom = 3, wordLength 2 = 1
+   *   for start = 24, end = 40 (spans codons 18-20), returns a BitSet with bits 17-19 set
+   * </pre>
+   * 
+   * @param start
+   * @param end
+   * @param sourceRange
+   * @param sourceWordLength
+   * @param targetWordLength
+   * @return
+   */
+  protected final static BitSet getMappedOffsetsForPositions(int start,
+          int end, List<int[]> sourceRange, int sourceWordLength, int targetWordLength)
+  {
+    BitSet overlaps = new BitSet();
+    int offset = 0;
+    final int s1 = sourceRange.size();
+    for (int i = 0; i < s1; i++)
+    {
+      int[] range = sourceRange.get(i);
+      final int offset1 = offset;
+      int overlapStartOffset = -1;
+      int overlapEndOffset = -1;
+
+      if (range[1] >= range[0])
+      {
+        /*
+         * forward direction range
+         */
+        if (start <= range[1] && end >= range[0])
+        {
+          /*
+           * overlap
+           */
+          int overlapStart = Math.max(start, range[0]);
+          overlapStartOffset = offset1 + overlapStart - range[0];
+          int overlapEnd = Math.min(end, range[1]);
+          overlapEndOffset = offset1 + overlapEnd - range[0];
+        }
+      }
+      else
+      {
+        /*
+         * reverse direction range
+         */
+        if (start <= range[0] && end >= range[1])
+        {
+          /*
+           * overlap
+           */
+          int overlapStart = Math.max(start, range[1]);
+          int overlapEnd = Math.min(end, range[0]);
+          overlapStartOffset = offset1 + range[0] - overlapEnd;
+          overlapEndOffset = offset1 + range[0] - overlapStart;
+        }
+      }
+
+      if (overlapStartOffset > -1)
+      {
+        /*
+         * found an overlap
+         */
+        if (sourceWordLength != targetWordLength)
+        {
+          /*
+           * convert any overlap found to whole words in the target range
+           * (e.g. treat any partial codon overlap as if the whole codon)
+           */
+          overlapStartOffset -= overlapStartOffset % sourceWordLength;
+          overlapStartOffset = overlapStartOffset / sourceWordLength
+                  * targetWordLength;
+
+          /*
+           * similar calculation for range end, adding 
+           * (wordLength2 - 1) for end of mapped word
+           */
+          overlapEndOffset -= overlapEndOffset % sourceWordLength;
+          overlapEndOffset = overlapEndOffset / sourceWordLength
+                  * targetWordLength;
+          overlapEndOffset += targetWordLength - 1;
+        }
+        overlaps.set(overlapStartOffset, overlapEndOffset + 1);
+      }
+      offset += 1 + Math.abs(range[1] - range[0]);
+    }
+    return overlaps;
+  }
+
+  /**
+   * Returns a (possibly empty) list of the [start-end] values (positions) at
+   * offsets in the {@code targetRange} list that are marked by 'on' bits in the
+   * {@code offsets} bitset.
+   * 
+   * @param targetRange
+   * @param offsets
+   * @return
+   */
+  protected final static List<int[]> getPositionsForOffsets(
+          List<int[]> targetRange, BitSet offsets)
+  {
+    List<int[]> mapped = new ArrayList<>();
+    if (offsets.isEmpty())
+    {
+      return mapped;
+    }
+
+    /*
+     * count of positions preceding ranges[i]
+     */
+    int traversed = 0;
+
+    /*
+     * for each [from-to] range in ranges:
+     * - find subranges (if any) at marked offsets
+     * - add the start-end values at the marked positions
+     */
+    final int toAdd = offsets.cardinality();
+    int added = 0;
+    final int s2 = targetRange.size();
+    for (int i = 0; added < toAdd && i < s2; i++)
+    {
+      int[] range = targetRange.get(i);
+      added += addOffsetPositions(mapped, traversed, range, offsets);
+      traversed += Math.abs(range[1] - range[0]) + 1;
+    }
+    return mapped;
+  }
+
+  /**
+   * Helper method that adds any start-end subranges of {@code range} that are
+   * at offsets in {@code range} marked by set bits in overlaps.
+   * {@code mapOffset} is added to {@code range} offset positions. Returns the
+   * count of positions added.
+   * 
+   * @param mapped
+   * @param mapOffset
+   * @param range
+   * @param overlaps
+   * @return
+   */
+  final static int addOffsetPositions(List<int[]> mapped,
+          final int mapOffset, final int[] range, final BitSet overlaps)
+  {
+    final int rangeLength = 1 + Math.abs(range[1] - range[0]);
+    final int step = range[1] < range[0] ? -1 : 1;
+    int offsetStart = 0; // offset into range
+    int added = 0;
+
+    while (offsetStart < rangeLength)
+    {
+      /*
+       * find the start of the next marked overlap offset;
+       * if there is none, or it is beyond range, then finished
+       */
+      int overlapStart = overlaps.nextSetBit(mapOffset + offsetStart);
+      if (overlapStart == -1 || overlapStart - mapOffset >= rangeLength)
+      {
+        /*
+         * no more overlaps, or no more within range[]
+         */
+        return added;
+      }
+      overlapStart -= mapOffset;
+
+      /*
+       * end of the overlap range is just before the next clear bit;
+       * restrict it to end of range if necessary;
+       * note we may add a reverse strand range here (end < start)
+       */
+      int overlapEnd = overlaps.nextClearBit(mapOffset + overlapStart + 1);
+      overlapEnd = (overlapEnd == -1) ? rangeLength - 1
+              : Math.min(rangeLength - 1, overlapEnd - mapOffset - 1);
+      int startPosition = range[0] + step * overlapStart;
+      int endPosition = range[0] + step * overlapEnd;
+      mapped.add(new int[] { startPosition, endPosition });
+      offsetStart = overlapEnd + 1;
+      added += Math.abs(endPosition - startPosition) + 1;
+    }
+
+    return added;
+  }
 }
index b552c21..4e07a08 100644 (file)
  */
 package jalview.util;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
 import jalview.analysis.AlignmentSorter;
 import jalview.api.AlignViewportI;
 import jalview.commands.CommandI;
@@ -39,13 +46,6 @@ import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
 /**
  * Helper methods for manipulations involving sequence mappings.
  * 
@@ -546,8 +546,7 @@ public final class MappingUtils
     while (regions.hasNext())
     {
       mapHiddenColumns(regions.next(), codonFrames, newHidden,
-              fromSequences,
-              toSequences, fromGapChar);
+              fromSequences, toSequences, fromGapChar);
     }
     return; // mappedColumns;
   }
@@ -965,7 +964,7 @@ public final class MappingUtils
 
     int min = Math.min(range[0], range[1]);
     int max = Math.max(range[0], range[1]);
-  
+
     return (min <= queryRange[0] && max >= queryRange[0]
             && min <= queryRange[1] && max >= queryRange[1]);
   }
@@ -980,8 +979,7 @@ public final class MappingUtils
    *          a list of (single) [start, end] ranges
    * @return
    */
-  public static void removeEndPositions(int positions,
-          List<int[]> ranges)
+  public static void removeEndPositions(int positions, List<int[]> ranges)
   {
     int toRemove = positions;
     Iterator<int[]> it = new ReverseListIterator<>(ranges);
@@ -993,8 +991,8 @@ public final class MappingUtils
         /*
          * not coded for [start1, end1, start2, end2, ...]
          */
-        System.err
-                .println("MappingUtils.removeEndPositions doesn't handle multiple  ranges");
+        System.err.println(
+                "MappingUtils.removeEndPositions doesn't handle multiple  ranges");
         return;
       }
 
@@ -1004,8 +1002,8 @@ public final class MappingUtils
         /*
          * not coded for a reverse strand range (end < start)
          */
-        System.err
-                .println("MappingUtils.removeEndPositions doesn't handle reverse strand");
+        System.err.println(
+                "MappingUtils.removeEndPositions doesn't handle reverse strand");
         return;
       }
       if (length > toRemove)
@@ -1020,4 +1018,25 @@ public final class MappingUtils
       }
     }
   }
+
+  /**
+   * Converts a list of {@code start-end} ranges to a single array of
+   * {@code start1, end1, start2, ... } ranges
+   * 
+   * @param ranges
+   * @return
+   */
+  public static int[] rangeListToArray(List<int[]> ranges)
+  {
+    int rangeCount = ranges.size();
+    int[] result = new int[rangeCount * 2];
+    int j = 0;
+    for (int i = 0; i < rangeCount; i++)
+    {
+      int[] range = ranges.get(i);
+      result[j++] = range[0];
+      result[j++] = range[1];
+    }
+    return result;
+  }
 }
index bb94566..ba7da10 100644 (file)
@@ -162,7 +162,7 @@ public class MessageManager
    */
   public static String getStringOrReturn(String keyroot, String name)
   {
-    String smkey = keyroot + name.toLowerCase().replaceAll(" ", "");
+    String smkey = keyroot + name.toLowerCase(Locale.ROOT).replaceAll(" ", "");
     try
     {
       name = rb.getString(smkey);
index 6713bbc..0f2d01d 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.util;
 
+import java.util.Locale;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.StringTokenizer;
@@ -103,7 +105,7 @@ public class ParseHtmlBodyAndLinks
       return;
     }
     StringBuilder sb = new StringBuilder(description.length());
-    if (description.toUpperCase().indexOf("<HTML>") == -1)
+    if (description.toUpperCase(Locale.ROOT).indexOf("<HTML>") == -1)
     {
       htmlContent = false;
     }
@@ -129,7 +131,7 @@ public class ParseHtmlBodyAndLinks
         token = token.substring(0, startTag);
       }
 
-      if (tag != null && tag.toUpperCase().startsWith("A HREF="))
+      if (tag != null && tag.toUpperCase(Locale.ROOT).startsWith("A HREF="))
       {
         if (token.length() > 0)
         {
index 71d798d..2d05a1b 100644 (file)
@@ -349,7 +349,7 @@ public class Platform
 
   public static byte[] getFileBytes(File f)
   {
-    return /** @j2sNative f && swingjs.JSUtil.getFileBytes$java_io_File(f) || */
+    return /** @j2sNative f && swingjs.JSUtil.getFileAsBytes$O(f) || */
     null;
   }
 
@@ -613,18 +613,21 @@ public class Platform
 
   public static void getURLCommandArguments()
   {
-
-    /**
-     * Retrieve the first query field as command arguments to Jalview. Include
-     * only if prior to "?j2s" or "&j2s" or "#". Assign the applet's __Info.args
-     * element to this value.
-     * 
-     * @j2sNative var a =
-     *            decodeURI((document.location.href.replace("&","?").split("?j2s")[0]
-     *            + "?").split("?")[1].split("#")[0]); a &&
-     *            (J2S.thisApplet.__Info.args = a.split(" "));
-     */
-
+      try {
+      /**
+       * Retrieve the first query field as command arguments to Jalview. Include
+       * only if prior to "?j2s" or "&j2s" or "#". Assign the applet's
+       * __Info.args element to this value.
+       * 
+       * @j2sNative var a =
+       *            decodeURI((document.location.href.replace("&","?").split("?j2s")[0]
+       *            + "?").split("?")[1].split("#")[0]); a && (System.out.println("URL arguments detected were "+a)) &&
+       *            (J2S.thisApplet.__Info.urlargs = a.split(" ")); 
+       *            (!J2S.thisApplet.__Info.args || J2S.thisApplet.__Info.args == "" || J2S.thisApplet.__Info.args == "??") && (J2S.thisApplet.__Info.args = a) && (System.out.println("URL arguments were passed to J2S main."));
+       */
+    } catch (Throwable t)
+    {
+    }
   }
 
   /**
index 1f114a8..bf5b87a 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.util;
 
+import java.util.Locale;
+
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.ArrayList;
@@ -409,9 +411,9 @@ public class StringUtils
     }
     if (s.length() <= 1)
     {
-      return s.toUpperCase();
+      return s.toUpperCase(Locale.ROOT);
     }
-    return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
+    return s.substring(0, 1).toUpperCase(Locale.ROOT) + s.substring(1).toLowerCase(Locale.ROOT);
   }
 
   /**
@@ -427,7 +429,7 @@ public class StringUtils
     {
       return null;
     }
-    String tmp2up = text.toUpperCase();
+    String tmp2up = text.toUpperCase(Locale.ROOT);
     int startTag = tmp2up.indexOf("<HTML>");
     if (startTag > -1)
     {
index 51e1828..e43ead2 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.util.matcher;
 
+import java.util.Locale;
+
 import java.util.Objects;
 
 /**
@@ -102,7 +104,7 @@ public class Matcher implements MatcherI
       patternType = PatternType.String;
     }
 
-    uppercasePattern = pattern == null ? null : pattern.toUpperCase();
+    uppercasePattern = pattern == null ? null : pattern.toUpperCase(Locale.ROOT);
 
     // if we add regex conditions (e.g. matchesPattern), then
     // pattern should hold the raw regex, and
@@ -172,7 +174,7 @@ public class Matcher implements MatcherI
   boolean matchesString(String compareTo)
   {
     boolean matched = false;
-    String upper = compareTo.toUpperCase().trim();
+    String upper = compareTo.toUpperCase(Locale.ROOT).trim();
     switch(condition) {
     case Matches:
       matched = upper.equals(uppercasePattern);
index 3608626..853bc3b 100644 (file)
@@ -104,11 +104,11 @@ public abstract class FeatureRendererModel
 
   Map<String, Float> featureOrder = null;
 
-  protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
-          this);
-
   protected AlignViewportI av;
 
+  private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+          this);
+
   @Override
   public AlignViewportI getViewport()
   {
@@ -301,13 +301,19 @@ public abstract class FeatureRendererModel
       {
         firing = Boolean.TRUE;
         findAllFeatures(true); // add all new features as visible
-        changeSupport.firePropertyChange("changeSupport", null, null);
+        notifyFeaturesChanged();
         firing = Boolean.FALSE;
       }
     }
   }
 
   @Override
+  public void notifyFeaturesChanged()
+  {
+    changeSupport.firePropertyChange("changeSupport", null, null);
+  }
+
+  @Override
   public List<SequenceFeature> findFeaturesAtColumn(SequenceI sequence, int column)
   {
     /*
index f44a2d1..d7da519 100644 (file)
@@ -29,9 +29,9 @@ import java.util.Set;
 
 public class FeaturesDisplayed implements FeaturesDisplayedI
 {
-  private Set<String> featuresDisplayed = new HashSet<String>();
+  private Set<String> featuresDisplayed = new HashSet<>();
 
-  private Set<String> featuresRegistered = new HashSet<String>();
+  private Set<String> featuresRegistered = new HashSet<>();
 
   public FeaturesDisplayed(FeaturesDisplayedI featuresDisplayed2)
   {
@@ -93,6 +93,13 @@ public class FeaturesDisplayed implements FeaturesDisplayedI
   }
 
   @Override
+  public void setHidden(String featureType)
+  {
+    featuresDisplayed.remove(featureType);
+    featuresRegistered.add(featureType);
+  }
+
+  @Override
   public boolean isRegistered(String type)
   {
     return featuresRegistered.contains(type);
index 7daa7b4..67b44d7 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws;
 
+import java.util.Locale;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Enumeration;
@@ -29,6 +31,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import jalview.analysis.AlignSeq;
 import jalview.api.FeatureSettingsModelI;
@@ -243,7 +247,7 @@ public class DBRefFetcher implements Runnable
    */
   void addSeqId(SequenceI seq, String key)
   {
-    key = key.toUpperCase();
+    key = key.toUpperCase(Locale.ROOT);
 
     Vector<SequenceI> seqs;
     if (seqRefs.containsKey(key))
@@ -397,17 +401,19 @@ public class DBRefFetcher implements Runnable
                DBRefEntry upref = uprefs.get(j);
                 addSeqId(sequence, upref.getAccessionId());
                 queries.addElement(
-                        upref.getAccessionId().toUpperCase());
+                        upref.getAccessionId().toUpperCase(Locale.ROOT));
               }
             }
             else
             {
+              Pattern possibleIds = Pattern.compile("[A-Za-z0-9_]+"); 
               // generate queries from sequence ID string
-              StringTokenizer st = new StringTokenizer(sequence.getName(),
-                      "|");
-              while (st.hasMoreTokens())
+              Matcher tokens = possibleIds.matcher(sequence.getName());
+              int p=0;
+              while (tokens.find(p))
               {
-                String token = st.nextToken();
+                String token = tokens.group();
+                p = tokens.end();
                 UPEntry[] presp = null;
                 if (picrClient != null)
                 {
@@ -436,7 +442,7 @@ public class DBRefFetcher implements Runnable
                           "Validated ID against PICR... (for what its worth):"
                                   + token);
                   addSeqId(sequence, token);
-                  queries.addElement(token.toUpperCase());
+                  queries.addElement(token.toUpperCase(Locale.ROOT));
                 }
                 else
                 {
@@ -444,7 +450,7 @@ public class DBRefFetcher implements Runnable
                   // System.out.println("Not querying source with
                   // token="+token+"\n");
                   addSeqId(sequence, token);
-                  queries.addElement(token.toUpperCase());
+                  queries.addElement(token.toUpperCase(Locale.ROOT));
                 }
               }
             }
@@ -540,7 +546,7 @@ public class DBRefFetcher implements Runnable
        DBRefEntry ref = entryRefs.get(j);
         String accessionId = ref.getAccessionId();
         // match up on accessionId
-        if (seqRefs.containsKey(accessionId.toUpperCase()))
+        if (seqRefs.containsKey(accessionId.toUpperCase(Locale.ROOT)))
         {
           Vector<SequenceI> seqs = seqRefs.get(accessionId);
           for (int jj = 0; jj < seqs.size(); jj++)
@@ -592,7 +598,7 @@ public class DBRefFetcher implements Runnable
       // sequenceMatches now contains the set of all sequences associated with
       // the returned db record
       final String retrievedSeqString = retrievedSeq.getSequenceAsString();
-      String entrySeq = retrievedSeqString.toUpperCase();
+      String entrySeq = retrievedSeqString.toUpperCase(Locale.ROOT);
       for (int m = 0; m < sequenceMatches.size(); m++)
       {
         sequence = sequenceMatches.elementAt(m);
@@ -611,7 +617,7 @@ public class DBRefFetcher implements Runnable
         boolean remoteEnclosesLocal = false;
         String nonGapped = AlignSeq
                 .extractGaps("-. ", sequence.getSequenceAsString())
-                .toUpperCase();
+                .toUpperCase(Locale.ROOT);
         int absStart = entrySeq.indexOf(nonGapped);
         if (absStart == -1)
         {
@@ -731,10 +737,10 @@ public class DBRefFetcher implements Runnable
               String ngAlsq = AlignSeq
                       .extractGaps("-. ",
                               alseqs[alsq].getSequenceAsString())
-                      .toUpperCase();
+                      .toUpperCase(Locale.ROOT);
               int oldstrt = alseqs[alsq].getStart();
               alseqs[alsq].setStart(sequence.getSequenceAsString()
-                      .toUpperCase().indexOf(ngAlsq) + sequence.getStart());
+                      .toUpperCase(Locale.ROOT).indexOf(ngAlsq) + sequence.getStart());
               if (oldstrt != alseqs[alsq].getStart())
               {
                 alseqs[alsq].setEnd(
index a480176..8fb69be 100644 (file)
@@ -27,6 +27,7 @@ import jalview.ws.dbsources.Pdb;
 import jalview.ws.dbsources.PfamFull;
 import jalview.ws.dbsources.PfamSeed;
 import jalview.ws.dbsources.RfamSeed;
+import jalview.ws.dbsources.TDBeacons;
 import jalview.ws.dbsources.Uniprot;
 import jalview.ws.seqfetcher.ASequenceFetcher;
 import jalview.ws.seqfetcher.DbSourceProxy;
@@ -54,6 +55,8 @@ public class SequenceFetcher extends ASequenceFetcher
     addDBRefSourceImpl(EmblSource.class);
     addDBRefSourceImpl(EmblCdsSource.class);
     addDBRefSourceImpl(Uniprot.class);
+    // not a sequence source yet
+    // addDBRefSourceImpl(TDBeacons.class);
     addDBRefSourceImpl(Pdb.class);
     addDBRefSourceImpl(PfamFull.class);
     addDBRefSourceImpl(PfamSeed.class);
diff --git a/src/jalview/ws/dbsources/EBIAlfaFold.java b/src/jalview/ws/dbsources/EBIAlfaFold.java
new file mode 100644 (file)
index 0000000..a93f233
--- /dev/null
@@ -0,0 +1,313 @@
+
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.dbsources;
+
+import jalview.api.FeatureSettingsModelI;
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.PDBEntry.Type;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.FileFormat;
+import jalview.io.FileFormatI;
+import jalview.io.FormatAdapter;
+import jalview.io.PDBFeatureSettings;
+import jalview.structure.StructureImportSettings;
+import jalview.util.HttpUtils;
+import jalview.util.MessageManager;
+import jalview.ws.ebi.EBIFetchClient;
+import jalview.ws.utils.UrlDownloadClient;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.stevesoft.pat.Regex;
+
+/**
+ * @author JimP
+ * 
+ */
+public class EBIAlfaFold extends EbiFileRetrievedProxy
+{
+  private static final String SEPARATOR = "|";
+
+  private static final String COLON = ":";
+
+  private static final int PDB_ID_LENGTH = 4;
+
+  public EBIAlfaFold()
+  {
+    super();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
+   */
+  @Override
+  public String getAccessionSeparator()
+  {
+    return null;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#getAccessionValidator()
+   */
+  @Override
+  public Regex getAccessionValidator()
+  {
+    Regex validator =  new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
+    validator.setIgnoreCase(true);
+    return validator;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#getDbSource()
+   */
+  @Override
+  public String getDbSource()
+  {
+    return "ALPHAFOLD";
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#getDbVersion()
+   */
+  @Override
+  public String getDbVersion()
+  {
+    return "1";
+  }
+
+  public static String getAlphaFoldCifDownloadUrl(String id)
+  {
+    return "https://alphafold.ebi.ac.uk/files/" + id + "-model_v1.cif";
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
+   */
+  @Override
+  public AlignmentI getSequenceRecords(String queries) throws Exception
+  {
+    AlignmentI pdbAlignment = null;
+    String chain = null;
+    String id = null;
+    if (queries.indexOf(COLON) > -1)
+    {
+      chain = queries.substring(queries.indexOf(COLON) + 1);
+      id = queries.substring(0, queries.indexOf(COLON));
+    }
+    else
+    {
+      id = queries;
+    }
+
+    if (!isValidReference(id))
+    {
+      System.err.println(
+              "(AFClient) Ignoring invalid pdb query: '" + id + "'");
+      stopQuery();
+      return null;
+    }
+    String alphaFoldCif = getAlphaFoldCifDownloadUrl(id);
+
+    try
+    {
+      File tmpFile = File.createTempFile(id, ".cif");
+      UrlDownloadClient.download(alphaFoldCif, tmpFile);
+      
+      // may not need this check ?
+      file = tmpFile.getAbsolutePath();
+      if (file == null)
+      {
+        return null;
+      }
+
+      pdbAlignment = importDownloadedStructureFromUrl(alphaFoldCif, tmpFile, id, chain, getDbSource(),getDbVersion());
+      
+
+      if (pdbAlignment == null || pdbAlignment.getHeight() < 1)
+      {
+        throw new Exception(MessageManager.formatMessage(
+                "exception.no_pdb_records_for_chain", new String[]
+                { id, ((chain == null) ? "' '" : chain) }));
+      }
+
+    } catch (Exception ex) // Problem parsing PDB file
+    {
+      stopQuery();
+      throw (ex);
+    }
+    return pdbAlignment;
+  }
+
+  /**
+   * general purpose structure importer - designed to yield alignment useful for transfer of annotation to associated sequences
+   * @param alphaFoldCif
+   * @param tmpFile
+   * @param id
+   * @param chain
+   * @param dbSource
+   * @param dbVersion
+   * @return
+   * @throws Exception
+   */
+  public static AlignmentI importDownloadedStructureFromUrl(String alphaFoldCif,
+          File tmpFile, String id, String chain, String dbSource, String dbVersion) throws Exception
+  {
+    String file = tmpFile.getAbsolutePath();
+    // todo get rid of Type and use FileFormatI instead?
+    FileFormatI fileFormat = FileFormat.MMCif;
+    AlignmentI pdbAlignment = new FormatAdapter().readFile(tmpFile,
+            DataSourceType.FILE, fileFormat);
+    if (pdbAlignment != null)
+    {
+      List<SequenceI> toremove = new ArrayList<SequenceI>();
+      for (SequenceI pdbcs : pdbAlignment.getSequences())
+      {
+        String chid = null;
+        // Mapping map=null;
+        for (PDBEntry pid : pdbcs.getAllPDBEntries())
+        {
+          if (pid.getFile() == file)
+          {
+            chid = pid.getChainCode();
+
+          }
+        }
+        if (chain == null || (chid != null && (chid.equals(chain)
+                || chid.trim().equals(chain.trim())
+                || (chain.trim().length() == 0 && chid.equals("_")))))
+        {
+          // FIXME seems to result in 'PDB|1QIP|1qip|A' - 1QIP is redundant.
+          // TODO: suggest simplify naming to 1qip|A as default name defined
+          pdbcs.setName(id + SEPARATOR + pdbcs.getName());
+          // Might need to add more metadata to the PDBEntry object
+          // like below
+          /*
+           * PDBEntry entry = new PDBEntry(); // Construct the PDBEntry
+           * entry.setId(id); if (entry.getProperty() == null)
+           * entry.setProperty(new Hashtable());
+           * entry.getProperty().put("chains", pdbchain.id + "=" +
+           * sq.getStart() + "-" + sq.getEnd());
+           * sq.getDatasetSequence().addPDBId(entry);
+           */
+          // Add PDB DB Refs
+          // We make a DBRefEtntry because we have obtained the PDB file from
+          // a
+          // verifiable source
+          // JBPNote - PDB DBRefEntry should also carry the chain and mapping
+          // information
+          if (dbSource != null)
+          {
+            DBRefEntry dbentry = new DBRefEntry(dbSource,
+
+                    dbVersion, (chid == null ? id : id + chid));
+            // dbentry.setMap()
+            pdbcs.addDBRef(dbentry);
+          }
+        }
+        else
+        {
+          // mark this sequence to be removed from the alignment
+          // - since it's not from the right chain
+          toremove.add(pdbcs);
+        }
+      }
+      // now remove marked sequences
+      for (SequenceI pdbcs : toremove)
+      {
+        pdbAlignment.deleteSequence(pdbcs);
+        if (pdbcs.getAnnotation() != null)
+        {
+          for (AlignmentAnnotation aa : pdbcs.getAnnotation())
+          {
+            pdbAlignment.deleteAnnotation(aa);
+          }
+        }
+      }
+    }
+    return pdbAlignment;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
+   */
+  @Override
+  public boolean isValidReference(String accession)
+  {
+    Regex r = getAccessionValidator();
+    return r.search(accession.trim());
+  }
+
+  /**
+   * human glyoxalase
+   */
+  @Override
+  public String getTestQuery()
+  {
+    return "1QIP";
+  }
+
+  @Override
+  public String getDbName()
+  {
+    return "PDB"; // getDbSource();
+  }
+
+  @Override
+  public int getTier()
+  {
+    return 0;
+  }
+
+  /**
+   * Returns a descriptor for suitable feature display settings with
+   * <ul>
+   * <li>ResNums or insertions features visible</li>
+   * <li>insertions features coloured red</li>
+   * <li>ResNum features coloured by label</li>
+   * <li>Insertions displayed above (on top of) ResNums</li>
+   * </ul>
+   */
+  @Override
+  public FeatureSettingsModelI getFeatureColourScheme()
+  {
+    return new PDBFeatureSettings();
+  }
+}
index a73af61..d02910c 100644 (file)
@@ -23,9 +23,7 @@ package jalview.ws.dbsources;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
 
-import com.stevesoft.pat.Regex;
-
-public class EmblCdsSource extends EmblXmlSource
+public class EmblCdsSource extends EmblFlatfileSource // was EmblXmlSource
 {
 
   public EmblCdsSource()
@@ -34,31 +32,12 @@ public class EmblCdsSource extends EmblXmlSource
   }
 
   @Override
-  public String getAccessionSeparator()
-  {
-    return null;
-  }
-
-  @Override
-  public Regex getAccessionValidator()
-  {
-    return new Regex("^[A-Z]+[0-9]+");
-  }
-
-  @Override
   public String getDbSource()
   {
     return DBRefSource.EMBLCDS;
   }
 
   @Override
-  public String getDbVersion()
-  {
-    return "0"; // TODO : this is dynamically set for a returned record - not
-    // tied to proxy
-  }
-
-  @Override
   public AlignmentI getSequenceRecords(String queries) throws Exception
   {
     if (queries.indexOf(".") > -1)
@@ -68,15 +47,6 @@ public class EmblCdsSource extends EmblXmlSource
     return getEmblSequenceRecords(DBRefSource.EMBLCDS, queries);
   }
 
-  @Override
-  public boolean isValidReference(String accession)
-  {
-    // most embl CDS refs look like ..
-    // TODO: improve EMBLCDS regex
-    return (accession == null || accession.length() < 2) ? false
-            : getAccessionValidator().search(accession);
-  }
-
   /**
    * cDNA for LDHA_CHICK swissprot sequence
    */
@@ -92,10 +62,4 @@ public class EmblCdsSource extends EmblXmlSource
     return "EMBL (CDS)";
   }
 
-  @Override
-  public int getTier()
-  {
-    return 0;
-  }
-
 }
diff --git a/src/jalview/ws/dbsources/EmblFlatfileSource.java b/src/jalview/ws/dbsources/EmblFlatfileSource.java
new file mode 100644 (file)
index 0000000..2058800
--- /dev/null
@@ -0,0 +1,122 @@
+package jalview.ws.dbsources;
+
+import java.util.Locale;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.stevesoft.pat.Regex;
+
+import jalview.bin.Cache;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceI;
+import jalview.io.DataSourceType;
+import jalview.io.EmblFlatFile;
+import jalview.io.FileParse;
+import jalview.ws.ebi.EBIFetchClient;
+
+/**
+ * A class that does partial parsing of an EMBL flatfile.
+ * 
+ * @author gmcarstairs
+ *
+ */
+public abstract class EmblFlatfileSource extends EbiFileRetrievedProxy
+{
+  private static final Regex ACCESSION_REGEX = new Regex("^[A-Z]+[0-9]+");
+
+  @Override
+  public String getDbVersion()
+  {
+    return "0";
+  }
+
+  @Override
+  public String getAccessionSeparator()
+  {
+    return null;
+  }
+
+  @Override
+  public Regex getAccessionValidator()
+  {
+    return ACCESSION_REGEX;
+  }
+
+  @Override
+  public boolean isValidReference(String accession)
+  {
+    if (accession == null || accession.length() < 2)
+    {
+      return false;
+    }
+    return getAccessionValidator().search(accession);
+  }
+
+  @Override
+  public AlignmentI getSequenceRecords(String queries) throws Exception
+  {
+    return null;
+  }
+
+  @Override
+  public int getTier()
+  {
+    return 0;
+  }
+
+  protected AlignmentI getEmblSequenceRecords(String dbName, String query)
+          throws Exception
+  {
+    startQuery();
+    EBIFetchClient dbFetch = new EBIFetchClient();
+    File reply;
+    try
+    {
+      reply = dbFetch.fetchDataAsFile(
+              dbName.toLowerCase(Locale.ROOT) + ":" + query.trim(), null, "gz");
+    } catch (Exception e)
+    {
+      stopQuery();
+      throw new Exception(
+              String.format("EBI EMBL retrieval failed for %s:%s",
+                      dbName.toLowerCase(Locale.ROOT), query.trim()),
+              e);
+    }
+    return getEmblSequenceRecords(dbName, query, reply);
+  }
+
+  private AlignmentI getEmblSequenceRecords(String dbName, String query,
+          File reply) throws IOException
+  {
+    AlignmentI al = null;
+
+    if (reply != null && reply.exists())
+    {
+      file = reply.getAbsolutePath();
+      FileParse fp = new FileParse(file, DataSourceType.FILE);
+      EmblFlatFile emblParser = new EmblFlatFile(fp, getDbSource());
+      SequenceI[] seqs = emblParser.getSeqsAsArray();
+      if (seqs.length > 0)
+      {
+        al = new Alignment(seqs);
+      }
+
+      if (al == null)
+      {
+        Cache.log.error(
+                "No record found for '" + dbName + ":" + query + "'");
+      }
+    }
+
+    stopQuery();
+    return al;
+  }
+
+  @Override
+  public boolean isDnaCoding()
+  {
+    return true;
+  }
+}
index 6bbe2e1..df43bc3 100644 (file)
@@ -23,13 +23,11 @@ package jalview.ws.dbsources;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefSource;
 
-import com.stevesoft.pat.Regex;
-
 /**
  * @author JimP
  * 
  */
-public class EmblSource extends EmblXmlSource
+public class EmblSource extends EmblFlatfileSource // was EmblXmlSource
 {
 
   public EmblSource()
@@ -40,29 +38,6 @@ public class EmblSource extends EmblXmlSource
   /*
    * (non-Javadoc)
    * 
-   * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
-   */
-  @Override
-  public String getAccessionSeparator()
-  {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.ws.DbSourceProxy#getAccessionValidator()
-   */
-  @Override
-  public Regex getAccessionValidator()
-  {
-    return new Regex("^[A-Z]+[0-9]+");
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
    * @see jalview.ws.DbSourceProxy#getDbSource()
    */
   @Override
@@ -74,18 +49,6 @@ public class EmblSource extends EmblXmlSource
   /*
    * (non-Javadoc)
    * 
-   * @see jalview.ws.DbSourceProxy#getDbVersion()
-   */
-  @Override
-  public String getDbVersion()
-  {
-    // TODO Auto-generated method stub
-    return "0";
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
    * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
    */
   @Override
@@ -94,21 +57,6 @@ public class EmblSource extends EmblXmlSource
     return getEmblSequenceRecords(DBRefSource.EMBL, queries);
   }
 
-  /*
-   * (non-Javadoc)
-   * 
-   * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
-   */
-  @Override
-  public boolean isValidReference(String accession)
-  {
-    // most embl refs look like ..
-
-    return (accession == null || accession.length() < 2) ? false
-            : getAccessionValidator().search(accession);
-
-  }
-
   /**
    * return LHD_CHICK coding gene
    */
@@ -123,10 +71,4 @@ public class EmblSource extends EmblXmlSource
   {
     return "EMBL"; // getDbSource();
   }
-
-  @Override
-  public int getTier()
-  {
-    return 0;
-  }
 }
index 19366e0..c2d661b 100644 (file)
  */
 package jalview.ws.dbsources;
 
+import java.util.Locale;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import com.stevesoft.pat.Regex;
+
 import jalview.analysis.SequenceIdMatcher;
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
@@ -35,36 +58,23 @@ import jalview.util.DBRefUtils;
 import jalview.util.DnaUtils;
 import jalview.util.MapList;
 import jalview.util.MappingUtils;
-import jalview.util.MessageManager;
 import jalview.ws.ebi.EBIFetchClient;
 import jalview.xml.binding.embl.EntryType;
 import jalview.xml.binding.embl.EntryType.Feature;
 import jalview.xml.binding.embl.EntryType.Feature.Qualifier;
-import jalview.xml.binding.jalview.JalviewModel;
 import jalview.xml.binding.embl.ROOT;
 import jalview.xml.binding.embl.XrefType;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.stream.FactoryConfigurationError;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamReader;
-
+/**
+ * Provides XML binding and parsing of EMBL or EMBLCDS records retrieved from
+ * (e.g.) {@code https://www.ebi.ac.uk/ena/data/view/x53828&display=xml}.
+ * 
+ * @deprecated endpoint withdrawn August 2020 (JAL-3692), use EmblFlatfileSource
+ */
 public abstract class EmblXmlSource extends EbiFileRetrievedProxy
 {
+  private static final Regex ACCESSION_REGEX = new Regex("^[A-Z]+[0-9]+");
+
   /*
    * JAL-1856 Embl returns this text for query not found
    */
@@ -94,14 +104,15 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
     try
     {
       reply = dbFetch.fetchDataAsFile(
-              emprefx.toLowerCase() + ":" + query.trim(), "display=xml",
+              emprefx.toLowerCase(Locale.ROOT) + ":" + query.trim(), "display=xml",
               "xml");
     } catch (Exception e)
     {
       stopQuery();
-      throw new Exception(MessageManager.formatMessage(
-              "exception.ebiembl_retrieval_failed_on", new String[]
-              { emprefx.toLowerCase(), query.trim() }), e);
+      throw new Exception(
+              String.format("EBI EMBL XML retrieval failed for %s:%s",
+                      emprefx.toLowerCase(Locale.ROOT), query.trim()),
+              e);
     }
     return getEmblSequenceRecords(emprefx, query, reply);
   }
@@ -183,7 +194,8 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
       XMLStreamReader streamReader = XMLInputFactory.newInstance()
               .createXMLStreamReader(is);
       javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
-      JAXBElement<ROOT> rootElement =  um.unmarshal(streamReader, ROOT.class);
+      JAXBElement<ROOT> rootElement = um.unmarshal(streamReader,
+              ROOT.class);
       ROOT root = rootElement.getValue();
 
       /*
@@ -564,6 +576,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
               proteinSeq = new Sequence(proteinSeqName,
                       product.getSequenceAsString());
               matcher.add(proteinSeq);
+              proteinSeq.setDescription(product.getDescription());
               peptides.add(proteinSeq);
             }
             dnaToProteinMapping.setTo(proteinSeq);
@@ -617,8 +630,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
               && dnaToProteinMapping.getTo() != null)
       {
         DBRefEntry dnaToEmblProteinRef = new DBRefEntry(
-                DBRefSource.EMBLCDSProduct, sequenceVersion,
-                proteinId);
+                DBRefSource.EMBLCDSProduct, sequenceVersion, proteinId);
         dnaToEmblProteinRef.setMap(dnaToProteinMapping);
         dnaToProteinMapping.setMappedFromId(proteinId);
         dna.addDBRef(dnaToEmblProteinRef);
@@ -647,7 +659,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
     {
       return new int[] {};
     }
-  
+
     try
     {
       List<int[]> ranges = DnaUtils.parseLocation(location);
@@ -711,6 +723,40 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
     return sf;
   }
 
+  @Override
+  public String getAccessionSeparator()
+  {
+    return null;
+  }
+
+  @Override
+  public Regex getAccessionValidator()
+  {
+    return ACCESSION_REGEX;
+  }
+
+  @Override
+  public String getDbVersion()
+  {
+    return "0";
+  }
+
+  @Override
+  public int getTier()
+  {
+    return 0;
+  }
+
+  @Override
+  public boolean isValidReference(String accession)
+  {
+    if (accession == null || accession.length() < 2)
+    {
+      return false;
+    }
+    return getAccessionValidator().search(accession);
+  }
+
   /**
    * Truncates (if necessary) the exon intervals to match 3 times the length of
    * the protein; also accepts 3 bases longer (for stop codon not included in
@@ -729,7 +775,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
     }
     int expectedCdsLength = proteinLength * 3;
     int exonLength = MappingUtils.getLength(Arrays.asList(exon));
-  
+
     /*
      * if exon length matches protein, or is shorter, or longer by the 
      * length of a stop codon (3 bases), then leave it unchanged
@@ -739,7 +785,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
     {
       return exon;
     }
-  
+
     int origxon[];
     int sxpos = -1;
     int endxon = 0;
@@ -759,7 +805,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
           // .println("Truncating final exon interval on region by "
           // + (cdspos - cdslength));
         }
-  
+
         /*
          * shrink the final exon - reduce end position if forward
          * strand, increase it if reverse
@@ -775,7 +821,7 @@ public abstract class EmblXmlSource extends EbiFileRetrievedProxy
         break;
       }
     }
-  
+
     if (sxpos != -1)
     {
       // and trim the exon interval set if necessary
diff --git a/src/jalview/ws/dbsources/TDBeacons.java b/src/jalview/ws/dbsources/TDBeacons.java
new file mode 100644 (file)
index 0000000..345e217
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.ws.dbsources;
+
+import java.util.Locale;
+
+import jalview.bin.Cache;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.schemes.ResidueProperties;
+import jalview.util.StringUtils;
+import jalview.ws.seqfetcher.DbSourceProxyImpl;
+import jalview.xml.binding.embl.ROOT;
+import jalview.xml.binding.uniprot.DbReferenceType;
+import jalview.xml.binding.uniprot.Entry;
+import jalview.xml.binding.uniprot.FeatureType;
+import jalview.xml.binding.uniprot.LocationType;
+import jalview.xml.binding.uniprot.PositionType;
+import jalview.xml.binding.uniprot.PropertyType;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import com.stevesoft.pat.Regex;
+
+/**
+ * This class queries the Uniprot database for sequence data, unmarshals the
+ * returned XML, and converts it to Jalview Sequence records (including attached
+ * database references and sequence features)
+ * 
+ * @author JimP
+ * 
+ */
+public class TDBeacons extends DbSourceProxyImpl
+{
+  private static final String DEFAULT_UNIPROT_DOMAIN = "https://www.uniprot.org";
+
+  private static final String BAR_DELIMITER = "|";
+  
+  private static final String DEFAULT_THREEDBEACONS_DOMAIN = "https://wwwdev.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons-hub-api/uniprot/summary/";
+
+  /**
+   * Constructor
+   */
+  public TDBeacons()
+  {
+    super();
+  }
+
+  private String getDomain()
+  {
+    return Cache.getDefault("UNIPROT_DOMAIN", DEFAULT_UNIPROT_DOMAIN);
+    //return Cache.getDefault("3DB_DOMAIN", DEFAULT_THREEDBEACONS_DOMAIN );
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#getAccessionSeparator()
+   */
+  @Override
+  public String getAccessionSeparator()
+  {
+    return null;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#getAccessionValidator()
+   */
+  @Override
+  public Regex getAccessionValidator()
+  {
+    return new Regex("([A-Z]+[0-9]+[A-Z0-9]+|[A-Z0-9]+_[A-Z0-9]+)");
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#getDbSource()
+   */
+  @Override
+  public String getDbSource()
+  {
+    return "3d-beacons";// DBRefSource.UNIPROT;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#getDbVersion()
+   */
+  @Override
+  public String getDbVersion()
+  {
+    return "0"; // we really don't know what version we're on.
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#getSequenceRecords(java.lang.String[])
+   */
+  @Override
+  public AlignmentI getSequenceRecords(String queries) throws Exception
+  {
+    startQuery();
+    try
+    {
+      queries = queries.toUpperCase(Locale.ROOT).replaceAll(
+              "(UNIPROT\\|?|UNIPROT_|UNIREF\\d+_|UNIREF\\d+\\|?)", "");
+      AlignmentI al = null;
+
+      String downloadstring = getDomain() + "/uniprot/" + queries
+              + ".xml";
+//      String downloadstring = getDomain() + queries + ".json";
+
+      URL url = new URL(downloadstring);
+      URLConnection urlconn = url.openConnection();
+      InputStream istr = urlconn.getInputStream();
+      List<Entry> entries = getUniprotEntries(istr);
+      if (entries != null)
+      {
+        List<SequenceI> seqs = new ArrayList<>();
+        for (Entry entry : entries)
+        {
+          seqs.add(uniprotEntryToSequence(entry));
+        }
+        al = new Alignment(seqs.toArray(new SequenceI[seqs.size()]));
+      }
+
+      stopQuery();
+      return al;
+    } catch (Exception e)
+    {
+      throw (e);
+    } finally
+    {
+      stopQuery();
+    }
+  }
+
+  /**
+   * Converts an Entry object (bound from Uniprot XML) to a Jalview Sequence
+   * 
+   * @param entry
+   * @return
+   */
+  SequenceI uniprotEntryToSequence(Entry entry)
+  {
+    String id = getUniprotEntryId(entry);
+    /*
+     * Sequence should not include any whitespace, but JAXB leaves these in
+     */
+    String seqString = entry.getSequence().getValue().replaceAll("\\s*",
+            "");
+
+    SequenceI sequence = new Sequence(id,
+            seqString);
+    sequence.setDescription(getUniprotEntryDescription(entry));
+
+    /*
+     * add a 'self' DBRefEntry for each accession
+     */
+    final String dbVersion = getDbVersion();
+    List<DBRefEntry> dbRefs = new ArrayList<>();
+    for (String accessionId : entry.getAccession())
+    {
+      DBRefEntry dbRef = new DBRefEntry(DBRefSource.UNIPROT, dbVersion,
+              accessionId);
+      dbRefs.add(dbRef);
+    }
+
+    /*
+     * add a DBRefEntry for each dbReference element in the XML;
+     * also add a PDBEntry if type="PDB";
+     * also add an EMBLCDS dbref if protein sequence id is given
+     * also add an Ensembl dbref " " " " " "
+     */
+    Vector<PDBEntry> pdbRefs = new Vector<>();
+    for (DbReferenceType dbref : entry.getDbReference())
+    {
+      String type = dbref.getType();
+      DBRefEntry dbr = new DBRefEntry(type,
+              DBRefSource.UNIPROT + ":" + dbVersion, dbref.getId());
+      dbRefs.add(dbr);
+      if ("PDB".equals(type))
+      {
+        pdbRefs.add(new PDBEntry(dbr));
+      }
+      if ("EMBL".equals(type))
+      {
+        /*
+         * e.g. Uniprot accession Q9BXM7 has
+         * <dbReference type="EMBL" id="M19359">
+         *   <property type="protein sequence ID" value="AAA40981.1"/>
+         *   <property type="molecule type" value="Genomic_DNA"/>
+         * </dbReference> 
+         */
+        String cdsId = getProperty(dbref.getProperty(),
+                "protein sequence ID");
+        if (cdsId != null && cdsId.trim().length() > 0)
+        {
+          // remove version
+          String[] vrs = cdsId.split("\\.");
+          String version = vrs.length > 1 ? vrs[1]
+                  : DBRefSource.UNIPROT + ":" + dbVersion;
+          dbr = new DBRefEntry(DBRefSource.EMBLCDS, version, vrs[0]);
+          dbRefs.add(dbr);
+        }
+      }
+      if ("Ensembl".equals(type))
+      {
+        /*
+         * e.g. Uniprot accession Q9BXM7 has
+         * <dbReference type="Ensembl" id="ENST00000321556">
+         *   <molecule id="Q9BXM7-1"/>
+         *   <property type="protein sequence ID" value="ENSP00000364204"/>
+         *   <property type="gene ID" value="ENSG00000158828"/>
+         * </dbReference> 
+         */
+        String cdsId = getProperty(dbref.getProperty(),
+                "protein sequence ID");
+        if (cdsId != null && cdsId.trim().length() > 0)
+        {
+          dbr = new DBRefEntry(DBRefSource.ENSEMBL,
+                  DBRefSource.UNIPROT + ":" + dbVersion, cdsId.trim());
+          dbRefs.add(dbr);
+        }
+      }
+    }
+
+    /*
+     * create features; they have either begin and end, or position, in XML
+     */
+    sequence.setPDBId(pdbRefs);
+    if (entry.getFeature() != null)
+    {
+      for (FeatureType uf : entry.getFeature())
+      {
+        LocationType location = uf.getLocation();
+        int start = 0;
+        int end = 0;
+        if (location.getPosition() != null)
+        {
+          start = location.getPosition().getPosition().intValue();
+          end = start;
+        }
+        else
+        {
+          start = location.getBegin().getPosition().intValue();
+          end = location.getEnd().getPosition().intValue();
+        }
+        SequenceFeature sf = new SequenceFeature(uf.getType(),
+                getDescription(uf), start, end, "Uniprot");
+        sf.setStatus(uf.getStatus());
+        sequence.addSequenceFeature(sf);
+      }
+    }
+    for (DBRefEntry dbr : dbRefs)
+    {
+      sequence.addDBRef(dbr);
+    }
+    return sequence;
+  }
+
+  /**
+   * A helper method that builds a sequence feature description
+   * 
+   * @param feature
+   * @return
+   */
+  static String getDescription(FeatureType feature)
+  {
+    String orig = feature.getOriginal();
+    List<String> variants = feature.getVariation();
+    StringBuilder sb = new StringBuilder();
+
+    /*
+     * append variant in standard format if present
+     * e.g. p.Arg59Lys
+     * multiple variants are split over lines using <br>
+     */
+    boolean asHtml = false;
+    if (orig != null && !orig.isEmpty() && variants != null
+            && !variants.isEmpty())
+    {
+      int p = 0;
+      for (String var : variants)
+      {
+        // TODO proper HGVS nomenclature for delins structural variations
+        // http://varnomen.hgvs.org/recommendations/protein/variant/delins/
+        // for now we are pragmatic - any orig/variant sequence longer than
+        // three characters is shown with single-character notation rather than
+        // three-letter notation
+        sb.append("p.");
+        if (orig.length() < 4)
+        {
+          for (int c = 0, clen = orig.length(); c < clen; c++)
+          {
+            char origchar = orig.charAt(c);
+            String orig3 = ResidueProperties.aa2Triplet.get("" + origchar);
+            sb.append(orig3 == null ? origchar
+                    : StringUtils.toSentenceCase(orig3));
+          }
+        }
+        else
+        {
+          sb.append(orig);
+        }
+
+        LocationType location = feature.getLocation();
+        PositionType start = location.getPosition() == null
+                ? location.getBegin()
+                : location.getPosition();
+        sb.append(Integer.toString(start.getPosition().intValue()));
+
+        if (var.length() < 4)
+        {
+          for (int c = 0, clen = var.length(); c < clen; c++)
+          {
+            char varchar = var.charAt(c);
+            String var3 = ResidueProperties.aa2Triplet.get("" + varchar);
+
+            sb.append(var3 != null ? StringUtils.toSentenceCase(var3)
+                    : "" + varchar);
+          }
+        }
+        else
+        {
+          sb.append(var);
+        }
+        if (++p != variants.size())
+        {
+          sb.append("<br/>&nbsp;&nbsp;");
+          asHtml = true;
+        }
+        else
+        {
+          sb.append(" ");
+        }
+      }
+    }
+    String description = feature.getDescription();
+    if (description != null)
+    {
+      sb.append(description);
+    }
+    if (asHtml)
+    {
+      sb.insert(0, "<html>");
+      sb.append("</html>");
+    }
+
+    return sb.toString();
+  }
+
+  /**
+   * A helper method that searches the list of properties for one with the given
+   * key, and if found returns the property value, else returns null
+   * 
+   * @param properties
+   * @param key
+   * @return
+   */
+  static String getProperty(List<PropertyType> properties, String key)
+  {
+    String value = null;
+    if (properties != null)
+    {
+      for (PropertyType prop : properties)
+      {
+        if (key.equals(prop.getType()))
+        {
+          value = prop.getValue();
+          break;
+        }
+      }
+    }
+    return value;
+  }
+
+  /**
+   * Extracts xml element entry/protein/recommendedName/fullName
+   * 
+   * @param entry
+   * @return
+   */
+  static String getUniprotEntryDescription(Entry entry)
+  {
+    String desc = "";
+    if (entry.getProtein() != null
+            && entry.getProtein().getRecommendedName() != null)
+    {
+      // fullName is mandatory if recommendedName is present
+      desc = entry.getProtein().getRecommendedName().getFullName()
+              .getValue();
+    }
+    return desc;
+  }
+
+  /**
+   * Constructs a sequence id by concatenating all entry/name elements with '|'
+   * separator
+   * 
+   * @param entry
+   * @return
+   */
+  static String getUniprotEntryId(Entry entry)
+  {
+    StringBuilder name = new StringBuilder(32);
+    for (String n : entry.getName())
+    {
+      if (name.length() > 0)
+      {
+        name.append(BAR_DELIMITER);
+      }
+      name.append(n);
+    }
+    return name.toString();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see jalview.ws.DbSourceProxy#isValidReference(java.lang.String)
+   */
+  @Override
+  public boolean isValidReference(String accession)
+  {
+    // TODO: make the following a standard validator
+    return (accession == null || accession.length() < 2) ? false
+            : getAccessionValidator().search(accession);
+  }
+
+  /**
+   * return LDHA_CHICK uniprot entry
+   */
+  @Override
+  public String getTestQuery()
+  {
+    return "P00340";
+  }
+
+  @Override
+  public String getDbName()
+  {
+    return "Uniprot"; // getDbSource();
+  }
+
+  @Override
+  public int getTier()
+  {
+    return 0;
+  }
+
+  /**
+   * Reads the reply to the EBI Fetch Uniprot data query, unmarshals it to an
+   * Uniprot object, and returns the enclosed Entry objects, or null on any
+   * failure
+   * 
+   * @param is
+   * @return
+   */
+  public List<Entry> getUniprotEntries(InputStream is)
+  {
+    List<Entry> entries = null;
+    try
+    {
+      JAXBContext jc = JAXBContext
+              .newInstance("jalview.xml.binding.uniprot");
+      XMLStreamReader streamReader = XMLInputFactory.newInstance()
+              .createXMLStreamReader(is);
+      javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
+      JAXBElement<jalview.xml.binding.uniprot.Uniprot> uniprotElement = 
+                 um.unmarshal(streamReader, jalview.xml.binding.uniprot.Uniprot.class);
+      jalview.xml.binding.uniprot.Uniprot uniprot = uniprotElement.getValue();
+      
+      if (uniprot != null && !uniprot.getEntry().isEmpty())
+      {
+        entries = uniprot.getEntry();
+      }
+    } catch (JAXBException | XMLStreamException
+            | FactoryConfigurationError e)
+    {
+      e.printStackTrace();
+    }
+    return entries;
+  }
+}
index 6f5f033..b9fe52f 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.dbsources;
 
+import java.util.Locale;
+
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
@@ -32,7 +34,6 @@ import jalview.datamodel.SequenceI;
 import jalview.schemes.ResidueProperties;
 import jalview.util.StringUtils;
 import jalview.ws.seqfetcher.DbSourceProxyImpl;
-import jalview.xml.binding.embl.ROOT;
 import jalview.xml.binding.uniprot.DbReferenceType;
 import jalview.xml.binding.uniprot.Entry;
 import jalview.xml.binding.uniprot.FeatureType;
@@ -41,8 +42,8 @@ import jalview.xml.binding.uniprot.PositionType;
 import jalview.xml.binding.uniprot.PropertyType;
 
 import java.io.InputStream;
+import java.net.HttpURLConnection;
 import java.net.URL;
-import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Vector;
@@ -139,7 +140,7 @@ public class Uniprot extends DbSourceProxyImpl
     startQuery();
     try
     {
-      queries = queries.toUpperCase().replaceAll(
+      queries = queries.toUpperCase(Locale.ROOT).replaceAll(
               "(UNIPROT\\|?|UNIPROT_|UNIREF\\d+_|UNIREF\\d+\\|?)", "");
       AlignmentI al = null;
 
@@ -147,21 +148,27 @@ public class Uniprot extends DbSourceProxyImpl
               + ".xml";
 
       URL url = new URL(downloadstring);
-      URLConnection urlconn = url.openConnection();
-      InputStream istr = urlconn.getInputStream();
-      List<Entry> entries = getUniprotEntries(istr);
-      if (entries != null)
+      HttpURLConnection urlconn = (HttpURLConnection)url.openConnection();
+      // anything other than 200 means we don't have data
+      // TODO: JAL-3882 reuse the EnsemblRestClient's fair 
+      // use/backoff logic to retry when the server tells us to go away
+      if (urlconn.getResponseCode() == 200)
       {
-        List<SequenceI> seqs = new ArrayList<>();
-        for (Entry entry : entries)
+        InputStream istr = urlconn.getInputStream();
+        List<Entry> entries = getUniprotEntries(istr);
+        if (entries != null)
         {
-          seqs.add(uniprotEntryToSequence(entry));
+          List<SequenceI> seqs = new ArrayList<>();
+          for (Entry entry : entries)
+          {
+            seqs.add(uniprotEntryToSequence(entry));
+          }
+          al = new Alignment(seqs.toArray(new SequenceI[seqs.size()]));
         }
-        al = new Alignment(seqs.toArray(new SequenceI[seqs.size()]));
       }
-
       stopQuery();
       return al;
+      
     } catch (Exception e)
     {
       throw (e);
@@ -195,10 +202,12 @@ public class Uniprot extends DbSourceProxyImpl
      */
     final String dbVersion = getDbVersion();
     List<DBRefEntry> dbRefs = new ArrayList<>();
+    boolean canonical=true;
     for (String accessionId : entry.getAccession())
     {
       DBRefEntry dbRef = new DBRefEntry(DBRefSource.UNIPROT, dbVersion,
-              accessionId);
+              accessionId,null,canonical);
+      canonical=false;
       dbRefs.add(dbRef);
     }
 
@@ -514,6 +523,11 @@ public class Uniprot extends DbSourceProxyImpl
     } catch (JAXBException | XMLStreamException
             | FactoryConfigurationError e)
     {
+      if (e instanceof javax.xml.bind.UnmarshalException && e.getCause()!=null && e.getCause() instanceof XMLStreamException && e.getCause().getMessage().contains("[row,col]:[1,1]"))
+      {
+        // trying to parse an empty stream
+        return null;
+      }
       e.printStackTrace();
     }
     return entries;
index f0cb14b..9340f65 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.dbsources;
 
+import java.util.Locale;
+
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
@@ -75,11 +77,11 @@ public abstract class Xfam extends DbSourceProxyImpl
     {
       rcds.getSequenceAt(s).addDBRef(new DBRefEntry(getXfamSource(),
               // getDbSource(),
-              getDbVersion(), queries.trim().toUpperCase()));
+              getDbVersion(), queries.trim().toUpperCase(Locale.ROOT)));
       if (!getDbSource().equals(getXfamSource()))
       { // add the specific ref too
         rcds.getSequenceAt(s).addDBRef(new DBRefEntry(getDbSource(),
-                getDbVersion(), queries.trim().toUpperCase()));
+                getDbVersion(), queries.trim().toUpperCase(Locale.ROOT)));
       }
     }
     stopQuery();
@@ -88,7 +90,7 @@ public abstract class Xfam extends DbSourceProxyImpl
 
   String getURL(String queries)
   {
-    return getURLPrefix() + "/family/" + queries.trim().toUpperCase()
+    return getURLPrefix() + "/family/" + queries.trim().toUpperCase(Locale.ROOT)
             + getURLSuffix();
   }
 
index b5a7328..1d04351 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.ebi;
 
+import java.util.Locale;
+
 import jalview.datamodel.DBRefSource;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
@@ -91,7 +93,7 @@ public class EBIFetchClient
    *          the query formatted as db:query1;query2;query3
    * @param format
    *          the format wanted
-   * @param extension
+   * @param ext
    *          for the temporary file to hold response (without separator)
    * @return the file holding the response
    * @throws OutOfMemoryError
@@ -202,6 +204,7 @@ public class EBIFetchClient
   {
     String url = buildUrl(ids, database, format);
     InputStream is = null;
+    BufferedReader br = null;
     try
     {
       URL rcall = new URL(url);
@@ -215,7 +218,7 @@ public class EBIFetchClient
           Platform.streamToFile(is, outFile);
           return null;
         }
-        BufferedReader br = new BufferedReader(new InputStreamReader(is));
+        br = new BufferedReader(new InputStreamReader(is));
         String rtn;
         List<String> arl = new ArrayList<>();
         while ((rtn = br.readLine()) != null)
@@ -251,6 +254,15 @@ public class EBIFetchClient
         {
         }
       }
+      if (br != null)
+      {
+        try
+        {
+          br.close();
+        } catch (IOException e)
+        {
+        }
+      }
     }
     return null;
   }
@@ -269,13 +281,13 @@ public class EBIFetchClient
     if (database.equalsIgnoreCase(DBRefSource.EMBL)
             || database.equalsIgnoreCase(DBRefSource.EMBLCDS))
     {
-      url = "https://www.ebi.ac.uk/ena/data/view/" + ids.toLowerCase()
-              + (format != null ? "&" + format : "");
+      url = "https://www.ebi.ac.uk/ena/browser/api/embl/"
+              + ids.toLowerCase(Locale.ROOT) + "?download=true&gzip=true";
     }
     else
     {
       url = "https://www.ebi.ac.uk/Tools/dbfetch/dbfetch/"
-              + database.toLowerCase() + "/" + ids.toLowerCase()
+              + database.toLowerCase(Locale.ROOT) + "/" + ids.toLowerCase(Locale.ROOT)
               + (format != null ? "/" + format : "");
     }
     return url;
index 83252d8..7e6e660 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.io.mime;
 
+import java.util.Locale;
+
 import jalview.io.packed.DataProvider.JvDataType;
 
 /**
@@ -53,7 +55,7 @@ public class MimeTypes
    */
   public static JvDataType getTypeOf(String mimeType)
   {
-    String mt = mimeType.toLowerCase();
+    String mt = mimeType.toLowerCase(Locale.ROOT);
     for (int i = 0; i < typemap.length; i += 2)
     {
       if (typemap[i].equals(mt))
index 3b7bdb6..e9c2aea 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.jws1;
 
+import java.util.Locale;
+
 import jalview.analysis.AlignSeq;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentView;
@@ -183,7 +185,7 @@ public class JPredClient extends WS1Client
 
   private String getPredictionName(String webServiceName)
   {
-    if (webServiceName.toLowerCase()
+    if (webServiceName.toLowerCase(Locale.ROOT)
             .indexOf("secondary structure prediction") > -1)
     {
       return webServiceName;
index 4a09625..dad314b 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.jws1;
 
+import java.util.Locale;
+
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.gui.AlignFrame;
@@ -119,7 +121,7 @@ public class MsaWSClient extends WS1Client
 
     wsInfo.setProgressText(((submitGaps) ? "Re-alignment" : "Alignment")
             + " of " + altitle + "\nJob details\n");
-    String jobtitle = WebServiceName.toLowerCase();
+    String jobtitle = WebServiceName.toLowerCase(Locale.ROOT);
     if (jobtitle.endsWith("alignment"))
     {
       if (submitGaps && (!jobtitle.endsWith("realignment")
index 0f1a25e..71ba1b9 100644 (file)
  */
 package jalview.ws.jws2;
 
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.List;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuListener;
+
+import compbio.metadata.Argument;
 import jalview.api.AlignCalcWorkerI;
 import jalview.bin.Cache;
 import jalview.gui.AlignFrame;
@@ -34,18 +45,6 @@ import jalview.ws.jws2.jabaws2.Jws2Instance;
 import jalview.ws.params.WsParamSetI;
 import jalview.ws.uimodel.AlignAnalysisUIText;
 
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.List;
-
-import javax.swing.JCheckBoxMenuItem;
-import javax.swing.JMenu;
-import javax.swing.JMenuItem;
-import javax.swing.event.MenuEvent;
-import javax.swing.event.MenuListener;
-
-import compbio.metadata.Argument;
-
 /**
  * provides metadata for a jabaws2 service instance - resolves names, etc.
  * 
@@ -122,9 +121,10 @@ public abstract class Jws2Client extends jalview.ws.WSClient
       WsParamSetI prset = jobParams.getPreset();
       if (prset == null)
       {
-        paramset = jobParams.isServiceDefaults() ? null
-                : JabaParamStore
-                        .getJabafromJwsArgs(jobParams.getJobParams());
+        paramset =
+                /* JAL-3739 always take values from input form */
+                /* jobParams.isServiceDefaults() ? null : */
+                JabaParamStore.getJabafromJwsArgs(jobParams.getJobParams());
         this.preset = null;
       }
       else
index 23c6949..8ed4a36 100644 (file)
  */
 package jalview.ws.jws2;
 
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.AlignmentView;
-import jalview.gui.AlignFrame;
-import jalview.gui.Desktop;
-import jalview.gui.JvOptionPane;
-import jalview.gui.JvSwingUtils;
-import jalview.util.MessageManager;
-import jalview.ws.jws2.jabaws2.Jws2Instance;
-import jalview.ws.params.WsParamSetI;
+import java.util.Locale;
 
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -42,6 +34,15 @@ import javax.swing.ToolTipManager;
 
 import compbio.data.msa.MsaWS;
 import compbio.metadata.Argument;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentView;
+import jalview.gui.AlignFrame;
+import jalview.gui.Desktop;
+import jalview.gui.JvOptionPane;
+import jalview.gui.JvSwingUtils;
+import jalview.util.MessageManager;
+import jalview.ws.jws2.jabaws2.Jws2Instance;
+import jalview.ws.params.WsParamSetI;
 
 /**
  * DOCUMENT ME!
@@ -149,7 +150,7 @@ public class MsaWSClient extends Jws2Client
 
     wsInfo.setProgressText(((submitGaps) ? "Re-alignment" : "Alignment")
             + " of " + altitle + "\nJob details\n");
-    String jobtitle = WebServiceName.toLowerCase();
+    String jobtitle = WebServiceName.toLowerCase(Locale.ROOT);
     if (jobtitle.endsWith("alignment"))
     {
       if (submitGaps && (!jobtitle.endsWith("realignment")
@@ -214,9 +215,9 @@ public class MsaWSClient extends Jws2Client
 
   @Override
   public void attachWSMenuEntry(JMenu rmsawsmenu,
-          final Jws2Instance service, final AlignFrame alignFrame)
+          final Jws2Instance service, final AlignFrame af)
   {
-    if (registerAAConWSInstance(rmsawsmenu, service, alignFrame))
+    if (registerAAConWSInstance(rmsawsmenu, service, af))
     {
       // Alignment dependent analysis calculation WS gui
       return;
@@ -264,14 +265,14 @@ public class MsaWSClient extends Jws2Client
         @Override
         public void actionPerformed(ActionEvent e)
         {
-          AlignmentView msa = alignFrame.gatherSequencesForAlignment();
+          AlignmentView msa = af.gatherSequencesForAlignment();
 
           if (msa != null)
           {
-            new MsaWSClient(service, alignFrame.getTitle(), msa, withGaps,
+            new MsaWSClient(service, af.getTitle(), msa, withGaps,
                     true,
-                    alignFrame.getViewport().getAlignment().getDataset(),
-                    alignFrame);
+                    af.getViewport().getAlignment().getDataset(),
+                    af);
           }
 
         }
@@ -291,13 +292,10 @@ public class MsaWSClient extends Jws2Client
           @Override
           public void actionPerformed(ActionEvent e)
           {
-            AlignmentView msa = alignFrame.gatherSequencesForAlignment();
+            AlignmentView msa = af.gatherSequencesForAlignment();
             if (msa != null)
             {
-              new MsaWSClient(service, null, null, true,
-                      alignFrame.getTitle(), msa, withGaps, true,
-                      alignFrame.getViewport().getAlignment().getDataset(),
-                      alignFrame);
+              startJob(service, af, withGaps, msa);
             }
 
           }
@@ -312,9 +310,9 @@ public class MsaWSClient extends Jws2Client
 
           final int showToolTipFor = ToolTipManager.sharedInstance()
                   .getDismissDelay();
-          for (final WsParamSetI preset : presets)
+          for (final WsParamSetI preSet : presets)
           {
-            final JMenuItem methodR = new JMenuItem(preset.getName());
+            final JMenuItem methodR = new JMenuItem(preSet.getName());
             final int QUICK_TOOLTIP = 1500;
             // JAL-1582 shorten tooltip display time in these menu items as
             // they can obscure other options
@@ -336,27 +334,27 @@ public class MsaWSClient extends Jws2Client
 
             });
             String tooltip = JvSwingUtils.wrapTooltip(true, "<strong>"
-                    + (preset.isModifiable()
+                    + (preSet.isModifiable()
                             ? MessageManager.getString("label.user_preset")
                             : MessageManager
                                     .getString("label.service_preset"))
-                    + "</strong><br/>" + preset.getDescription());
+                    + "</strong><br/>" + preSet.getDescription());
             methodR.setToolTipText(tooltip);
             methodR.addActionListener(new ActionListener()
             {
               @Override
               public void actionPerformed(ActionEvent e)
               {
-                AlignmentView msa = alignFrame
+                AlignmentView msa = af
                         .gatherSequencesForAlignment();
 
                 if (msa != null)
                 {
-                  MsaWSClient msac = new MsaWSClient(service, preset,
-                          alignFrame.getTitle(), msa, false, true,
-                          alignFrame.getViewport().getAlignment()
+                  MsaWSClient msac = new MsaWSClient(service, preSet,
+                          af.getTitle(), msa, false, true,
+                          af.getViewport().getAlignment()
                                   .getDataset(),
-                          alignFrame);
+                          af);
                 }
 
               }
@@ -378,4 +376,20 @@ public class MsaWSClient extends Jws2Client
       }
     } while (!finished);
   }
+
+  protected void startJob(final Jws2Instance service, final AlignFrame af,
+          final boolean withGaps, AlignmentView msa)
+  {
+    try {
+    new MsaWSClient(service, null, null, true,
+            af.getTitle(), msa, withGaps, true,
+            af.getViewport().getAlignment().getDataset(),
+            af);
+    } catch (Exception e) {
+      JvOptionPane.showMessageDialog(alignFrame, e.getMessage(),
+              MessageManager.getString("label.state_job_error"),
+              JvOptionPane.WARNING_MESSAGE);
+
+    }
+  }
 }
index 45bddac..a30a09c 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.jws2;
 
+import java.util.Locale;
+
 import jalview.api.AlignCalcWorkerI;
 import jalview.gui.AlignFrame;
 import jalview.gui.Desktop;
@@ -128,7 +130,7 @@ public class SequenceAnnotationWSClient extends Jws2Client
         worker.updateParameters(this.preset, paramset);
       }
     }
-    if (sh.action.toLowerCase().contains("disorder"))
+    if (sh.action.toLowerCase(Locale.ROOT).contains("disorder"))
     {
       // build IUPred style client. take sequences, returns annotation per
       // sequence.
index cbfbd3b..5a3fb35 100644 (file)
  */
 package jalview.ws.jws2.dm;
 
-import jalview.util.MessageManager;
-import jalview.ws.jws2.ParameterUtils;
-import jalview.ws.params.OptionI;
-
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.List;
 
 import compbio.metadata.Option;
+import jalview.util.MessageManager;
+import jalview.ws.jws2.ParameterUtils;
+import jalview.ws.params.OptionI;
 
 public class JabaOption implements jalview.ws.params.OptionI
 {
@@ -94,9 +93,9 @@ public class JabaOption implements jalview.ws.params.OptionI
       opt.setDefaultValue(selectedItem);
     } catch (Exception e)
     {
-      e.printStackTrace();
-      throw new Error(MessageManager.getString(
-              "error.implementation_error_cannot_set_jaba_option"));
+      throw new IllegalArgumentException(MessageManager
+              .formatMessage("error.invalid_value_for_option", new String[]
+              { selectedItem, opt.getName() }));
     }
   }
 
index 88431a6..a96b6d9 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.rest;
 
+import java.util.Locale;
+
 import jalview.ws.params.ArgumentI;
 import jalview.ws.params.InvalidArgumentException;
 import jalview.ws.params.OptionI;
@@ -232,7 +234,7 @@ public abstract class InputType
         {
           valid = false;
           warnings.append("Invalid value for parameter "
-                  + mtch.group(1).toLowerCase() + " '" + mtch.group(2)
+                  + mtch.group(1).toLowerCase(Locale.ROOT) + " '" + mtch.group(2)
                   + "' (expected an integer)\n");
         }
 
index acb7904..20b517b 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.rest;
 
+import java.util.Locale;
+
 import jalview.bin.Cache;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
@@ -401,7 +403,7 @@ public class RestJobThread extends AWSThread
      */
     String f;
     StringBuffer content = new StringBuffer(f = EntityUtils.toString(en));
-    f = f.toLowerCase();
+    f = f.toLowerCase(Locale.ROOT);
     int body = f.indexOf("<body");
     if (body > -1)
     {
index ae58082..5a03aea 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.sifts;
 
+import java.util.Locale;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -219,7 +221,7 @@ public class SiftsClient implements SiftsClientI
     }
 
     String siftsFileName = SiftsSettings.getSiftDownloadDirectory()
-            + pdbId.toLowerCase() + ".xml.gz";
+            + pdbId.toLowerCase(Locale.ROOT) + ".xml.gz";
     File siftsFile = new File(siftsFileName);
     if (siftsFile.exists())
     {
@@ -233,7 +235,7 @@ public class SiftsClient implements SiftsClientI
         BackupFiles.moveFileToFile(siftsFile, oldSiftsFile);
         try
         {
-          siftsFile = downloadSiftsFile(pdbId.toLowerCase());
+          siftsFile = downloadSiftsFile(pdbId.toLowerCase(Locale.ROOT));
           oldSiftsFile.delete();
           return siftsFile;
         } catch (IOException e)
@@ -250,7 +252,7 @@ public class SiftsClient implements SiftsClientI
     }
     try
     {
-      siftsFile = downloadSiftsFile(pdbId.toLowerCase());
+      siftsFile = downloadSiftsFile(pdbId.toLowerCase(Locale.ROOT));
     } catch (IOException e)
     {
       throw new SiftsException(e.getMessage());
@@ -356,7 +358,7 @@ public class SiftsClient implements SiftsClientI
   public static boolean deleteSiftsFileByPDBId(String pdbId)
   {
     File siftsFile = new File(SiftsSettings.getSiftDownloadDirectory()
-            + pdbId.toLowerCase() + ".xml.gz");
+            + pdbId.toLowerCase(Locale.ROOT) + ".xml.gz");
     if (siftsFile.exists())
     {
       return siftsFile.delete();
@@ -431,7 +433,7 @@ public class SiftsClient implements SiftsClientI
         for (MapRegion mapRegion : mapRegions)
         {
           accessions
-                  .add(mapRegion.getDb().getDbAccessionId().toLowerCase());
+                  .add(mapRegion.getDb().getDbAccessionId().toLowerCase(Locale.ROOT));
         }
       }
     }
@@ -501,9 +503,9 @@ public class SiftsClient implements SiftsClientI
     HashSet<String> dbRefAccessionIdsString = new HashSet<String>();
     for (DBRefEntry dbref : seq.getDBRefs())
     {
-      dbRefAccessionIdsString.add(dbref.getAccessionId().toLowerCase());
+      dbRefAccessionIdsString.add(dbref.getAccessionId().toLowerCase(Locale.ROOT));
     }
-    dbRefAccessionIdsString.add(sourceDBRef.getAccessionId().toLowerCase());
+    dbRefAccessionIdsString.add(sourceDBRef.getAccessionId().toLowerCase(Locale.ROOT));
 
     curDBRefAccessionIdsString = dbRefAccessionIdsString;
     curSourceDBRef = sourceDBRef.getAccessionId();
@@ -891,14 +893,14 @@ public class SiftsClient implements SiftsClientI
   {
     boolean isStrictMatch = true;
     return isStrictMatch ? curSourceDBRef.equalsIgnoreCase(accession)
-            : curDBRefAccessionIdsString.contains(accession.toLowerCase());
+            : curDBRefAccessionIdsString.contains(accession.toLowerCase(Locale.ROOT));
   }
 
   private boolean isFoundInSiftsEntry(String accessionId)
   {
     Set<String> siftsDBRefs = getAllMappingAccession();
     return accessionId != null
-            && siftsDBRefs.contains(accessionId.toLowerCase());
+            && siftsDBRefs.contains(accessionId.toLowerCase(Locale.ROOT));
   }
 
   /**
index cb90984..e52eefb 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -20,43 +20,43 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for anonymous complex type.
+ * &lt;p&gt;Java class for anonymous complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;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="alcodon" maxOccurs="unbounded" minOccurs="0">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &lt;attribute name="pos1" type="{http://www.w3.org/2001/XMLSchema}integer" />
- *                 &lt;attribute name="pos2" type="{http://www.w3.org/2001/XMLSchema}integer" />
- *                 &lt;attribute name="pos3" type="{http://www.w3.org/2001/XMLSchema}integer" />
- *               &lt;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *         &lt;element name="alcodMap" maxOccurs="unbounded" minOccurs="0">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &lt;sequence>
- *                   &lt;element ref="{www.vamsas.ac.uk/jalview/version2}Mapping"/>
- *                 &lt;/sequence>
- *                 &lt;attribute name="dnasq" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
- *               &lt;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *       &lt;/sequence>
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="alcodon" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;attribute name="pos1" type="{http://www.w3.org/2001/XMLSchema}integer" /&amp;gt;
+ *                 &amp;lt;attribute name="pos2" type="{http://www.w3.org/2001/XMLSchema}integer" /&amp;gt;
+ *                 &amp;lt;attribute name="pos3" type="{http://www.w3.org/2001/XMLSchema}integer" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *         &amp;lt;element name="alcodMap" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;sequence&amp;gt;
+ *                   &amp;lt;element ref="{www.vamsas.ac.uk/jalview/version2}Mapping"/&amp;gt;
+ *                 &amp;lt;/sequence&amp;gt;
+ *                 &amp;lt;attribute name="dnasq" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -74,20 +74,20 @@ public class AlcodonFrame {
     /**
      * Gets the value of the alcodon property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 alcodon property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the alcodon property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getAlcodon().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link AlcodonFrame.Alcodon }
      * 
@@ -103,20 +103,20 @@ public class AlcodonFrame {
     /**
      * Gets the value of the alcodMap property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 alcodMap property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the alcodMap property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getAlcodMap().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link AlcodonFrame.AlcodMap }
      * 
@@ -131,22 +131,22 @@ public class AlcodonFrame {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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 ref="{www.vamsas.ac.uk/jalview/version2}Mapping"/>
-     *       &lt;/sequence>
-     *       &lt;attribute name="dnasq" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element ref="{www.vamsas.ac.uk/jalview/version2}Mapping"/&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attribute name="dnasq" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -220,21 +220,21 @@ public class AlcodonFrame {
      *                                                                 Element may have either all pos1,2,3 attributes specified, or none at all (indicating a gapped column with no translated peptide).
      *                                                         
      * 
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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="pos1" type="{http://www.w3.org/2001/XMLSchema}integer" />
-     *       &lt;attribute name="pos2" type="{http://www.w3.org/2001/XMLSchema}integer" />
-     *       &lt;attribute name="pos3" type="{http://www.w3.org/2001/XMLSchema}integer" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;attribute name="pos1" type="{http://www.w3.org/2001/XMLSchema}integer" /&amp;gt;
+     *       &amp;lt;attribute name="pos2" type="{http://www.w3.org/2001/XMLSchema}integer" /&amp;gt;
+     *       &amp;lt;attribute name="pos3" type="{http://www.w3.org/2001/XMLSchema}integer" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
index 35f78f3..6c1573f 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -19,61 +19,61 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for anonymous complex type.
+ * &lt;p&gt;Java class for anonymous complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;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 ref="{www.vamsas.ac.uk/jalview/version2}annotationElement" maxOccurs="unbounded" minOccurs="0"/>
- *         &lt;element name="label" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *         &lt;element name="description" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
- *         &lt;element name="thresholdLine" minOccurs="0">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &lt;attribute name="label" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                 &lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}float" />
- *                 &lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}int" />
- *               &lt;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *         &lt;element name="property" maxOccurs="unbounded" minOccurs="0">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &lt;attribute name="name" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                 &lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}string" />
- *               &lt;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *       &lt;/sequence>
- *       &lt;attribute name="graph" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
- *       &lt;attribute name="graphType" type="{http://www.w3.org/2001/XMLSchema}int" />
- *       &lt;attribute name="sequenceRef" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="groupRef" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="graphColour" type="{http://www.w3.org/2001/XMLSchema}int" />
- *       &lt;attribute name="graphGroup" type="{http://www.w3.org/2001/XMLSchema}int" />
- *       &lt;attribute name="graphHeight" type="{http://www.w3.org/2001/XMLSchema}int" />
- *       &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="scoreOnly" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
- *       &lt;attribute name="score" type="{http://www.w3.org/2001/XMLSchema}double" />
- *       &lt;attribute name="visible" type="{http://www.w3.org/2001/XMLSchema}boolean" />
- *       &lt;attribute name="centreColLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" />
- *       &lt;attribute name="scaleColLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" />
- *       &lt;attribute name="showAllColLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" />
- *       &lt;attribute name="autoCalculated" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
- *       &lt;attribute name="belowAlignment" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" />
- *       &lt;attribute name="calcId" type="{http://www.w3.org/2001/XMLSchema}string" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element ref="{www.vamsas.ac.uk/jalview/version2}annotationElement" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="label" type="{http://www.w3.org/2001/XMLSchema}string"/&amp;gt;
+ *         &amp;lt;element name="description" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="thresholdLine" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;attribute name="label" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *                 &amp;lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *         &amp;lt;element name="property" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;attribute name="name" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="graph" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *       &amp;lt;attribute name="graphType" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="sequenceRef" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="groupRef" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="graphColour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="graphGroup" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="graphHeight" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="scoreOnly" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *       &amp;lt;attribute name="score" type="{http://www.w3.org/2001/XMLSchema}double" /&amp;gt;
+ *       &amp;lt;attribute name="visible" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *       &amp;lt;attribute name="centreColLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *       &amp;lt;attribute name="scaleColLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *       &amp;lt;attribute name="showAllColLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *       &amp;lt;attribute name="autoCalculated" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *       &amp;lt;attribute name="belowAlignment" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+ *       &amp;lt;attribute name="calcId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -132,20 +132,20 @@ public class Annotation {
     /**
      * Gets the value of the annotationElement property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 annotationElement property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the annotationElement property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getAnnotationElement().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link AnnotationElement }
      * 
@@ -233,20 +233,20 @@ public class Annotation {
     /**
      * Gets the value of the property property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 property property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the property property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getProperty().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link Annotation.Property }
      * 
@@ -673,20 +673,20 @@ public class Annotation {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *       &lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;attribute name="name" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -751,21 +751,21 @@ public class Annotation {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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="label" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *       &lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}float" />
-     *       &lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}int" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;attribute name="label" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+     *       &amp;lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
index 6f5ef65..1b31b22 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -15,26 +15,26 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for AnnotationColourScheme complex type.
+ * &lt;p&gt;Java class for AnnotationColourScheme complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="AnnotationColourScheme">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;attribute name="aboveThreshold" type="{http://www.w3.org/2001/XMLSchema}int" />
- *       &lt;attribute name="annotation" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="minColour" type="{http://www.w3.org/2001/XMLSchema}int" />
- *       &lt;attribute name="maxColour" type="{http://www.w3.org/2001/XMLSchema}int" />
- *       &lt;attribute name="colourScheme" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" />
- *       &lt;attribute name="perSequence" type="{http://www.w3.org/2001/XMLSchema}boolean" />
- *       &lt;attribute name="predefinedColours" type="{http://www.w3.org/2001/XMLSchema}boolean" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="AnnotationColourScheme"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;attribute name="aboveThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="annotation" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="minColour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="maxColour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="colourScheme" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *       &amp;lt;attribute name="perSequence" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *       &amp;lt;attribute name="predefinedColours" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
index 6780323..d141272 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -16,32 +16,32 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for anonymous complex type.
+ * &lt;p&gt;Java class for anonymous complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;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="displayCharacter" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
- *         &lt;element name="description" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
- *         &lt;element name="secondaryStructure" minOccurs="0">
- *           &lt;simpleType>
- *             &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string">
- *               &lt;length value="1"/>
- *             &lt;/restriction>
- *           &lt;/simpleType>
- *         &lt;/element>
- *         &lt;element name="value" type="{http://www.w3.org/2001/XMLSchema}float" minOccurs="0"/>
- *       &lt;/sequence>
- *       &lt;attribute name="position" use="required" type="{http://www.w3.org/2001/XMLSchema}int" />
- *       &lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}int" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="displayCharacter" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="description" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="secondaryStructure" minOccurs="0"&amp;gt;
+ *           &amp;lt;simpleType&amp;gt;
+ *             &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}string"&amp;gt;
+ *               &amp;lt;length value="1"/&amp;gt;
+ *             &amp;lt;/restriction&amp;gt;
+ *           &amp;lt;/simpleType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *         &amp;lt;element name="value" type="{http://www.w3.org/2001/XMLSchema}float" minOccurs="0"/&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="position" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
index 5edb2e8..bff4959 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -18,25 +18,25 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for DoubleMatrix complex type.
+ * &lt;p&gt;Java class for DoubleMatrix complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="DoubleMatrix">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;sequence>
- *         &lt;element name="row" type="{www.jalview.org}DoubleVector" maxOccurs="unbounded" minOccurs="0"/>
- *         &lt;element name="D" type="{www.jalview.org}DoubleVector" minOccurs="0"/>
- *         &lt;element name="E" type="{www.jalview.org}DoubleVector" minOccurs="0"/>
- *       &lt;/sequence>
- *       &lt;attribute name="rows" type="{http://www.w3.org/2001/XMLSchema}int" />
- *       &lt;attribute name="columns" type="{http://www.w3.org/2001/XMLSchema}int" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="DoubleMatrix"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="row" type="{www.jalview.org}DoubleVector" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="D" type="{www.jalview.org}DoubleVector" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="E" type="{www.jalview.org}DoubleVector" minOccurs="0"/&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="rows" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="columns" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -61,20 +61,20 @@ public class DoubleMatrix {
     /**
      * Gets the value of the row property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 row property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the row property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getRow().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link DoubleVector }
      * 
index 68ebf9b..a255c74 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -17,21 +17,21 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for DoubleVector complex type.
+ * &lt;p&gt;Java class for DoubleVector complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="DoubleVector">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;sequence>
- *         &lt;element name="v" type="{http://www.w3.org/2001/XMLSchema}double" maxOccurs="unbounded" minOccurs="0"/>
- *       &lt;/sequence>
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="DoubleVector"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="v" type="{http://www.w3.org/2001/XMLSchema}double" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -47,20 +47,20 @@ public class DoubleVector {
     /**
      * Gets the value of the v property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 v property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the v property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getV().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link Double }
      * 
index 39fc0c3..73468c9 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -17,38 +17,38 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for feature complex type.
+ * &lt;p&gt;Java class for feature complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="feature">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;sequence>
- *         &lt;element name="otherData" maxOccurs="unbounded" minOccurs="0">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &lt;attribute name="key" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                 &lt;attribute name="key2" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                 &lt;attribute name="value" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
- *               &lt;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *       &lt;/sequence>
- *       &lt;attribute name="begin" 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="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="description" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="status" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="featureGroup" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="score" type="{http://www.w3.org/2001/XMLSchema}float" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="feature"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="otherData" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;attribute name="key" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="key2" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="value" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="begin" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="end" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *       &amp;lt;attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="description" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="status" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="featureGroup" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="score" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -77,20 +77,20 @@ public class Feature {
     /**
      * Gets the value of the otherData property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 otherData property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the otherData property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getOtherData().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link Feature.OtherData }
      * 
@@ -257,21 +257,21 @@ public class Feature {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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="key" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *       &lt;attribute name="key2" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *       &lt;attribute name="value" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;attribute name="key" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="key2" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="value" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
index c92f72c..dee9bc1 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -18,24 +18,24 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for FeatureMatcher complex type.
+ * &lt;p&gt;Java class for FeatureMatcher complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="FeatureMatcher">
- *   &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="condition" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *         &lt;element name="value" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *       &lt;/sequence>
- *       &lt;attribute name="by" type="{www.jalview.org/colours}FilterBy" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="FeatureMatcher"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="attributeName" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="2" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="condition" type="{http://www.w3.org/2001/XMLSchema}string"/&amp;gt;
+ *         &amp;lt;element name="value" type="{http://www.w3.org/2001/XMLSchema}string"/&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="by" type="{www.jalview.org/colours}FilterBy" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -59,20 +59,20 @@ public class FeatureMatcher {
     /**
      * Gets the value of the attributeName property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the attributeName property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getAttributeName().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link String }
      * 
index 0c21215..4c0848f 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -20,33 +20,33 @@ import javax.xml.bind.annotation.XmlType;
 /**
  * A feature match condition, which may be simple or compound
  * 
- * <p>Java class for FeatureMatcherSet complex type.
+ * &lt;p&gt;Java class for FeatureMatcherSet complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="FeatureMatcherSet">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;choice>
- *         &lt;element name="matchCondition" type="{www.jalview.org/colours}FeatureMatcher"/>
- *         &lt;element name="compoundMatcher">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &lt;sequence>
- *                   &lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet" maxOccurs="2" minOccurs="2"/>
- *                 &lt;/sequence>
- *                 &lt;attribute name="and" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
- *               &lt;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *       &lt;/choice>
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="FeatureMatcherSet"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;choice&amp;gt;
+ *         &amp;lt;element name="matchCondition" type="{www.jalview.org/colours}FeatureMatcher"/&amp;gt;
+ *         &amp;lt;element name="compoundMatcher"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;sequence&amp;gt;
+ *                   &amp;lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet" maxOccurs="2" minOccurs="2"/&amp;gt;
+ *                 &amp;lt;/sequence&amp;gt;
+ *                 &amp;lt;attribute name="and" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *       &amp;lt;/choice&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -112,22 +112,22 @@ public class FeatureMatcherSet {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet" maxOccurs="2" minOccurs="2"/>
-     *       &lt;/sequence>
-     *       &lt;attribute name="and" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet" maxOccurs="2" minOccurs="2"/&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attribute name="and" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -145,20 +145,20 @@ public class FeatureMatcherSet {
         /**
          * Gets the value of the matcherSet property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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 matcherSet property.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the matcherSet property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getMatcherSet().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link FeatureMatcherSet }
          * 
index 36b454f..bbb84a4 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -14,19 +14,18 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for FilterBy.
+ * &lt;p&gt;Java class for FilterBy.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
- * <p>
- * <pre>
- * &lt;simpleType name="FilterBy">
- *   &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string">
- *     &lt;enumeration value="byLabel"/>
- *     &lt;enumeration value="byScore"/>
- *     &lt;enumeration value="byAttribute"/>
- *   &lt;/restriction>
- * &lt;/simpleType>
- * </pre>
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
+ * &lt;pre&gt;
+ * &amp;lt;simpleType name="FilterBy"&amp;gt;
+ *   &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}string"&amp;gt;
+ *     &amp;lt;enumeration value="byLabel"/&amp;gt;
+ *     &amp;lt;enumeration value="byScore"/&amp;gt;
+ *     &amp;lt;enumeration value="byAttribute"/&amp;gt;
+ *   &amp;lt;/restriction&amp;gt;
+ * &amp;lt;/simpleType&amp;gt;
+ * &lt;/pre&gt;
  * 
  */
 @XmlType(name = "FilterBy", namespace = "www.jalview.org/colours")
index 0ea2491..90819d7 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -24,341 +24,341 @@ import javax.xml.datatype.XMLGregorianCalendar;
 
 
 /**
- * <p>Java class for JalviewModel complex type.
+ * &lt;p&gt;Java class for JalviewModel complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="JalviewModel">
- *   &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;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>
- *           &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;attribute name="linkToAllViews" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
- *                 &lt;/restriction>
- *               &lt;/complexContent>
- *             &lt;/complexType>
- *           &lt;/element>
- *           &lt;element name="PcaViewer" maxOccurs="unbounded" minOccurs="0">
- *             &lt;complexType>
- *               &lt;complexContent>
- *                 &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                   &lt;sequence>
- *                     &lt;element name="sequencePoint" maxOccurs="unbounded">
- *                       &lt;complexType>
- *                         &lt;complexContent>
- *                           &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                             &lt;attGroup ref="{www.jalview.org}position"/>
- *                             &lt;attribute name="sequenceRef" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                           &lt;/restriction>
- *                         &lt;/complexContent>
- *                       &lt;/complexType>
- *                     &lt;/element>
- *                     &lt;element name="axis" maxOccurs="3" minOccurs="3">
- *                       &lt;complexType>
- *                         &lt;complexContent>
- *                           &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                             &lt;attGroup ref="{www.jalview.org}position"/>
- *                           &lt;/restriction>
- *                         &lt;/complexContent>
- *                       &lt;/complexType>
- *                     &lt;/element>
- *                     &lt;element name="seqPointMin">
- *                       &lt;complexType>
- *                         &lt;complexContent>
- *                           &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                             &lt;attGroup ref="{www.jalview.org}position"/>
- *                           &lt;/restriction>
- *                         &lt;/complexContent>
- *                       &lt;/complexType>
- *                     &lt;/element>
- *                     &lt;element name="seqPointMax">
- *                       &lt;complexType>
- *                         &lt;complexContent>
- *                           &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                             &lt;attGroup ref="{www.jalview.org}position"/>
- *                           &lt;/restriction>
- *                         &lt;/complexContent>
- *                       &lt;/complexType>
- *                     &lt;/element>
- *                     &lt;element name="pcaData" type="{www.jalview.org}PcaDataType"/>
- *                   &lt;/sequence>
- *                   &lt;attGroup ref="{www.jalview.org}swingwindow"/>
- *                   &lt;attGroup ref="{www.jalview.org}SimilarityParams"/>
- *                   &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                   &lt;attribute name="scoreModelName" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                   &lt;attribute name="xDim" type="{http://www.w3.org/2001/XMLSchema}int" />
- *                   &lt;attribute name="yDim" type="{http://www.w3.org/2001/XMLSchema}int" />
- *                   &lt;attribute name="zDim" type="{http://www.w3.org/2001/XMLSchema}int" />
- *                   &lt;attribute name="bgColour" type="{http://www.w3.org/2001/XMLSchema}int" />
- *                   &lt;attribute name="scaleFactor" type="{http://www.w3.org/2001/XMLSchema}float" />
- *                   &lt;attribute name="showLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" />
- *                   &lt;attribute name="linkToAllViews" type="{http://www.w3.org/2001/XMLSchema}boolean" />
- *                 &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>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="JalviewModel"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="creationDate" type="{http://www.w3.org/2001/XMLSchema}dateTime"/&amp;gt;
+ *         &amp;lt;element name="version" type="{http://www.w3.org/2001/XMLSchema}string"/&amp;gt;
+ *         &amp;lt;element name="vamsasModel" type="{www.vamsas.ac.uk/jalview/version2}VAMSAS"/&amp;gt;
+ *         &amp;lt;sequence&amp;gt;
+ *           &amp;lt;element name="JSeq" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *             &amp;lt;complexType&amp;gt;
+ *               &amp;lt;complexContent&amp;gt;
+ *                 &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                   &amp;lt;sequence&amp;gt;
+ *                     &amp;lt;element name="features" type="{www.jalview.org}feature" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+ *                     &amp;lt;element name="pdbids" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *                       &amp;lt;complexType&amp;gt;
+ *                         &amp;lt;complexContent&amp;gt;
+ *                           &amp;lt;extension base="{www.jalview.org}pdbentry"&amp;gt;
+ *                             &amp;lt;sequence&amp;gt;
+ *                               &amp;lt;element name="structureState" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *                                 &amp;lt;complexType&amp;gt;
+ *                                   &amp;lt;simpleContent&amp;gt;
+ *                                     &amp;lt;extension base="&amp;lt;http://www.w3.org/2001/XMLSchema&amp;gt;string"&amp;gt;
+ *                                       &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+ *                                       &amp;lt;attribute name="visible" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                                       &amp;lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                                       &amp;lt;attribute name="alignwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+ *                                       &amp;lt;attribute name="colourwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                                       &amp;lt;attribute name="colourByJmol" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+ *                                       &amp;lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                                     &amp;lt;/extension&amp;gt;
+ *                                   &amp;lt;/simpleContent&amp;gt;
+ *                                 &amp;lt;/complexType&amp;gt;
+ *                               &amp;lt;/element&amp;gt;
+ *                             &amp;lt;/sequence&amp;gt;
+ *                           &amp;lt;/extension&amp;gt;
+ *                         &amp;lt;/complexContent&amp;gt;
+ *                       &amp;lt;/complexType&amp;gt;
+ *                     &amp;lt;/element&amp;gt;
+ *                     &amp;lt;element name="hiddenSequences" type="{http://www.w3.org/2001/XMLSchema}int" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+ *                     &amp;lt;element name="rnaViewer" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *                       &amp;lt;complexType&amp;gt;
+ *                         &amp;lt;complexContent&amp;gt;
+ *                           &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                             &amp;lt;sequence&amp;gt;
+ *                               &amp;lt;element name="secondaryStructure" maxOccurs="unbounded"&amp;gt;
+ *                                 &amp;lt;complexType&amp;gt;
+ *                                   &amp;lt;complexContent&amp;gt;
+ *                                     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                                       &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                                       &amp;lt;attribute name="annotationId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                                       &amp;lt;attribute name="gapped" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                                       &amp;lt;attribute name="viewerState" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                                     &amp;lt;/restriction&amp;gt;
+ *                                   &amp;lt;/complexContent&amp;gt;
+ *                                 &amp;lt;/complexType&amp;gt;
+ *                               &amp;lt;/element&amp;gt;
+ *                             &amp;lt;/sequence&amp;gt;
+ *                             &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+ *                             &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                             &amp;lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                             &amp;lt;attribute name="dividerLocation" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                             &amp;lt;attribute name="selectedRna" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                           &amp;lt;/restriction&amp;gt;
+ *                         &amp;lt;/complexContent&amp;gt;
+ *                       &amp;lt;/complexType&amp;gt;
+ *                     &amp;lt;/element&amp;gt;
+ *                   &amp;lt;/sequence&amp;gt;
+ *                   &amp;lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="start" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="end" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="hidden" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="viewreference" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                 &amp;lt;/restriction&amp;gt;
+ *               &amp;lt;/complexContent&amp;gt;
+ *             &amp;lt;/complexType&amp;gt;
+ *           &amp;lt;/element&amp;gt;
+ *           &amp;lt;element name="JGroup" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *             &amp;lt;complexType&amp;gt;
+ *               &amp;lt;complexContent&amp;gt;
+ *                 &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                   &amp;lt;sequence&amp;gt;
+ *                     &amp;lt;element name="seq" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded"/&amp;gt;
+ *                     &amp;lt;element name="annotationColours" type="{www.jalview.org}AnnotationColourScheme" minOccurs="0"/&amp;gt;
+ *                   &amp;lt;/sequence&amp;gt;
+ *                   &amp;lt;attribute name="start" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="end" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="name" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="consThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="pidThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="outlineColour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="displayBoxes" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="displayText" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="colourText" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="textCol1" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="textCol2" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="textColThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="showUnconserved" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="ignoreGapsinConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+ *                   &amp;lt;attribute name="showConsensusHistogram" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+ *                   &amp;lt;attribute name="showSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                   &amp;lt;attribute name="normaliseSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                   &amp;lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;/restriction&amp;gt;
+ *               &amp;lt;/complexContent&amp;gt;
+ *             &amp;lt;/complexType&amp;gt;
+ *           &amp;lt;/element&amp;gt;
+ *           &amp;lt;element name="Viewport" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *             &amp;lt;complexType&amp;gt;
+ *               &amp;lt;complexContent&amp;gt;
+ *                 &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                   &amp;lt;sequence&amp;gt;
+ *                     &amp;lt;element name="AnnotationColours" type="{www.jalview.org}AnnotationColourScheme" minOccurs="0"/&amp;gt;
+ *                     &amp;lt;element name="hiddenColumns" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *                       &amp;lt;complexType&amp;gt;
+ *                         &amp;lt;complexContent&amp;gt;
+ *                           &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                             &amp;lt;attribute name="start" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                             &amp;lt;attribute name="end" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                           &amp;lt;/restriction&amp;gt;
+ *                         &amp;lt;/complexContent&amp;gt;
+ *                       &amp;lt;/complexType&amp;gt;
+ *                     &amp;lt;/element&amp;gt;
+ *                     &amp;lt;element name="calcIdParam" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *                       &amp;lt;complexType&amp;gt;
+ *                         &amp;lt;complexContent&amp;gt;
+ *                           &amp;lt;extension base="{www.jalview.org/xml/wsparamset}WebServiceParameterSet"&amp;gt;
+ *                             &amp;lt;attribute name="calcId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                             &amp;lt;attribute name="needsUpdate" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                             &amp;lt;attribute name="autoUpdate" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                           &amp;lt;/extension&amp;gt;
+ *                         &amp;lt;/complexContent&amp;gt;
+ *                       &amp;lt;/complexType&amp;gt;
+ *                     &amp;lt;/element&amp;gt;
+ *                   &amp;lt;/sequence&amp;gt;
+ *                   &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+ *                   &amp;lt;attribute name="conservationSelected" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="pidSelected" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="bgColour" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="consThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="pidThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="showFullId" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="rightAlignIds" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="showText" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="showColourText" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="showUnconserved" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                   &amp;lt;attribute name="showBoxes" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="wrapAlignment" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="renderGaps" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="showSequenceFeatures" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="showNPfeatureTooltip" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="showDbRefTooltip" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="followHighlight" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+ *                   &amp;lt;attribute name="followSelection" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+ *                   &amp;lt;attribute name="showAnnotation" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="centreColumnLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                   &amp;lt;attribute name="showGroupConservation" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                   &amp;lt;attribute name="showGroupConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                   &amp;lt;attribute name="showConsensusHistogram" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+ *                   &amp;lt;attribute name="showSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                   &amp;lt;attribute name="normaliseSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                   &amp;lt;attribute name="ignoreGapsinConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+ *                   &amp;lt;attribute name="startRes" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="startSeq" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="fontName" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="fontSize" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="fontStyle" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="scaleProteinAsCdna" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+ *                   &amp;lt;attribute name="viewName" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="sequenceSetId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="gatheredViews" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="textCol1" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="textCol2" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="textColThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" /&amp;gt;
+ *                   &amp;lt;attribute name="complementId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="showComplementFeatures" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                   &amp;lt;attribute name="showComplementFeaturesOnTop" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                 &amp;lt;/restriction&amp;gt;
+ *               &amp;lt;/complexContent&amp;gt;
+ *             &amp;lt;/complexType&amp;gt;
+ *           &amp;lt;/element&amp;gt;
+ *           &amp;lt;element name="UserColours" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *             &amp;lt;complexType&amp;gt;
+ *               &amp;lt;complexContent&amp;gt;
+ *                 &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                   &amp;lt;sequence&amp;gt;
+ *                     &amp;lt;element name="UserColourScheme" type="{www.jalview.org/colours}JalviewUserColours"/&amp;gt;
+ *                   &amp;lt;/sequence&amp;gt;
+ *                   &amp;lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;/restriction&amp;gt;
+ *               &amp;lt;/complexContent&amp;gt;
+ *             &amp;lt;/complexType&amp;gt;
+ *           &amp;lt;/element&amp;gt;
+ *           &amp;lt;element name="tree" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *             &amp;lt;complexType&amp;gt;
+ *               &amp;lt;complexContent&amp;gt;
+ *                 &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                   &amp;lt;sequence minOccurs="0"&amp;gt;
+ *                     &amp;lt;element name="title" type="{http://www.w3.org/2001/XMLSchema}string"/&amp;gt;
+ *                     &amp;lt;element name="newick" type="{http://www.w3.org/2001/XMLSchema}string"/&amp;gt;
+ *                   &amp;lt;/sequence&amp;gt;
+ *                   &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+ *                   &amp;lt;attribute name="fontName" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="fontSize" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="fontStyle" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *                   &amp;lt;attribute name="showBootstrap" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="showDistances" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="markUnlinked" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="fitToWindow" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="currentTree" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" /&amp;gt;
+ *                   &amp;lt;attribute name="linkToAllViews" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                 &amp;lt;/restriction&amp;gt;
+ *               &amp;lt;/complexContent&amp;gt;
+ *             &amp;lt;/complexType&amp;gt;
+ *           &amp;lt;/element&amp;gt;
+ *           &amp;lt;element name="PcaViewer" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *             &amp;lt;complexType&amp;gt;
+ *               &amp;lt;complexContent&amp;gt;
+ *                 &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                   &amp;lt;sequence&amp;gt;
+ *                     &amp;lt;element name="sequencePoint" maxOccurs="unbounded"&amp;gt;
+ *                       &amp;lt;complexType&amp;gt;
+ *                         &amp;lt;complexContent&amp;gt;
+ *                           &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                             &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+ *                             &amp;lt;attribute name="sequenceRef" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                           &amp;lt;/restriction&amp;gt;
+ *                         &amp;lt;/complexContent&amp;gt;
+ *                       &amp;lt;/complexType&amp;gt;
+ *                     &amp;lt;/element&amp;gt;
+ *                     &amp;lt;element name="axis" maxOccurs="3" minOccurs="3"&amp;gt;
+ *                       &amp;lt;complexType&amp;gt;
+ *                         &amp;lt;complexContent&amp;gt;
+ *                           &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                             &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+ *                           &amp;lt;/restriction&amp;gt;
+ *                         &amp;lt;/complexContent&amp;gt;
+ *                       &amp;lt;/complexType&amp;gt;
+ *                     &amp;lt;/element&amp;gt;
+ *                     &amp;lt;element name="seqPointMin"&amp;gt;
+ *                       &amp;lt;complexType&amp;gt;
+ *                         &amp;lt;complexContent&amp;gt;
+ *                           &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                             &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+ *                           &amp;lt;/restriction&amp;gt;
+ *                         &amp;lt;/complexContent&amp;gt;
+ *                       &amp;lt;/complexType&amp;gt;
+ *                     &amp;lt;/element&amp;gt;
+ *                     &amp;lt;element name="seqPointMax"&amp;gt;
+ *                       &amp;lt;complexType&amp;gt;
+ *                         &amp;lt;complexContent&amp;gt;
+ *                           &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                             &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+ *                           &amp;lt;/restriction&amp;gt;
+ *                         &amp;lt;/complexContent&amp;gt;
+ *                       &amp;lt;/complexType&amp;gt;
+ *                     &amp;lt;/element&amp;gt;
+ *                     &amp;lt;element name="pcaData" type="{www.jalview.org}PcaDataType"/&amp;gt;
+ *                   &amp;lt;/sequence&amp;gt;
+ *                   &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+ *                   &amp;lt;attGroup ref="{www.jalview.org}SimilarityParams"/&amp;gt;
+ *                   &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="scoreModelName" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                   &amp;lt;attribute name="xDim" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="yDim" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="zDim" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="bgColour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                   &amp;lt;attribute name="scaleFactor" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *                   &amp;lt;attribute name="showLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                   &amp;lt;attribute name="linkToAllViews" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                 &amp;lt;/restriction&amp;gt;
+ *               &amp;lt;/complexContent&amp;gt;
+ *             &amp;lt;/complexType&amp;gt;
+ *           &amp;lt;/element&amp;gt;
+ *           &amp;lt;element name="FeatureSettings" minOccurs="0"&amp;gt;
+ *             &amp;lt;complexType&amp;gt;
+ *               &amp;lt;complexContent&amp;gt;
+ *                 &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                   &amp;lt;sequence&amp;gt;
+ *                     &amp;lt;element name="setting" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *                       &amp;lt;complexType&amp;gt;
+ *                         &amp;lt;complexContent&amp;gt;
+ *                           &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                             &amp;lt;sequence&amp;gt;
+ *                               &amp;lt;element name="attributeName" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="2" minOccurs="0"/&amp;gt;
+ *                               &amp;lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet" minOccurs="0"/&amp;gt;
+ *                             &amp;lt;/sequence&amp;gt;
+ *                             &amp;lt;attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                             &amp;lt;attribute name="colour" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                             &amp;lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                             &amp;lt;attribute name="order" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *                             &amp;lt;attribute name="mincolour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                             &amp;lt;attribute name="noValueColour" type="{www.jalview.org/colours}NoValueColour" default="Min" /&amp;gt;
+ *                             &amp;lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *                             &amp;lt;attribute name="threshstate" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                             &amp;lt;attribute name="max" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *                             &amp;lt;attribute name="min" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *                             &amp;lt;attribute name="colourByLabel" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                             &amp;lt;attribute name="autoScale" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                           &amp;lt;/restriction&amp;gt;
+ *                         &amp;lt;/complexContent&amp;gt;
+ *                       &amp;lt;/complexType&amp;gt;
+ *                     &amp;lt;/element&amp;gt;
+ *                     &amp;lt;element name="group" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *                       &amp;lt;complexType&amp;gt;
+ *                         &amp;lt;complexContent&amp;gt;
+ *                           &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                             &amp;lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                             &amp;lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                           &amp;lt;/restriction&amp;gt;
+ *                         &amp;lt;/complexContent&amp;gt;
+ *                       &amp;lt;/complexType&amp;gt;
+ *                     &amp;lt;/element&amp;gt;
+ *                   &amp;lt;/sequence&amp;gt;
+ *                 &amp;lt;/restriction&amp;gt;
+ *               &amp;lt;/complexContent&amp;gt;
+ *             &amp;lt;/complexType&amp;gt;
+ *           &amp;lt;/element&amp;gt;
+ *         &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -473,20 +473,20 @@ public class JalviewModel {
     /**
      * Gets the value of the jSeq property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the jSeq property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getJSeq().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link JalviewModel.JSeq }
      * 
@@ -502,20 +502,20 @@ public class JalviewModel {
     /**
      * Gets the value of the jGroup property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the jGroup property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getJGroup().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link JalviewModel.JGroup }
      * 
@@ -531,20 +531,20 @@ public class JalviewModel {
     /**
      * Gets the value of the viewport property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the viewport property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getViewport().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link JalviewModel.Viewport }
      * 
@@ -560,20 +560,20 @@ public class JalviewModel {
     /**
      * Gets the value of the userColours property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the userColours property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getUserColours().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link JalviewModel.UserColours }
      * 
@@ -589,20 +589,20 @@ public class JalviewModel {
     /**
      * Gets the value of the tree property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the tree property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getTree().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link JalviewModel.Tree }
      * 
@@ -618,20 +618,20 @@ public class JalviewModel {
     /**
      * Gets the value of the pcaViewer property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 pcaViewer property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the pcaViewer property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getPcaViewer().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link JalviewModel.PcaViewer }
      * 
@@ -670,54 +670,54 @@ public class JalviewModel {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element name="setting" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+     *           &amp;lt;complexType&amp;gt;
+     *             &amp;lt;complexContent&amp;gt;
+     *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *                 &amp;lt;sequence&amp;gt;
+     *                   &amp;lt;element name="attributeName" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="2" minOccurs="0"/&amp;gt;
+     *                   &amp;lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet" minOccurs="0"/&amp;gt;
+     *                 &amp;lt;/sequence&amp;gt;
+     *                 &amp;lt;attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *                 &amp;lt;attribute name="colour" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *                 &amp;lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *                 &amp;lt;attribute name="order" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+     *                 &amp;lt;attribute name="mincolour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *                 &amp;lt;attribute name="noValueColour" type="{www.jalview.org/colours}NoValueColour" default="Min" /&amp;gt;
+     *                 &amp;lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+     *                 &amp;lt;attribute name="threshstate" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *                 &amp;lt;attribute name="max" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+     *                 &amp;lt;attribute name="min" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+     *                 &amp;lt;attribute name="colourByLabel" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *                 &amp;lt;attribute name="autoScale" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *               &amp;lt;/restriction&amp;gt;
+     *             &amp;lt;/complexContent&amp;gt;
+     *           &amp;lt;/complexType&amp;gt;
+     *         &amp;lt;/element&amp;gt;
+     *         &amp;lt;element name="group" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+     *           &amp;lt;complexType&amp;gt;
+     *             &amp;lt;complexContent&amp;gt;
+     *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *                 &amp;lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *                 &amp;lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *               &amp;lt;/restriction&amp;gt;
+     *             &amp;lt;/complexContent&amp;gt;
+     *           &amp;lt;/complexType&amp;gt;
+     *         &amp;lt;/element&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -736,20 +736,20 @@ public class JalviewModel {
         /**
          * Gets the value of the setting property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the setting property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getSetting().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link JalviewModel.FeatureSettings.Setting }
          * 
@@ -765,20 +765,20 @@ public class JalviewModel {
         /**
          * Gets the value of the group property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the group property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getGroup().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link JalviewModel.FeatureSettings.Group }
          * 
@@ -793,20 +793,20 @@ public class JalviewModel {
 
 
         /**
-         * <p>Java class for anonymous complex type.
+         * &lt;p&gt;Java class for anonymous complex type.
          * 
-         * <p>The following schema fragment specifies the expected content contained within this class.
+         * &lt;p&gt;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>
+         * &lt;pre&gt;
+         * &amp;lt;complexType&amp;gt;
+         *   &amp;lt;complexContent&amp;gt;
+         *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+         *       &amp;lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *       &amp;lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+         *     &amp;lt;/restriction&amp;gt;
+         *   &amp;lt;/complexContent&amp;gt;
+         * &amp;lt;/complexType&amp;gt;
+         * &lt;/pre&gt;
          * 
          * 
          */
@@ -863,34 +863,34 @@ public class JalviewModel {
 
 
         /**
-         * <p>Java class for anonymous complex type.
+         * &lt;p&gt;Java class for anonymous complex type.
          * 
-         * <p>The following schema fragment specifies the expected content contained within this class.
+         * &lt;p&gt;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>
+         * &lt;pre&gt;
+         * &amp;lt;complexType&amp;gt;
+         *   &amp;lt;complexContent&amp;gt;
+         *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+         *       &amp;lt;sequence&amp;gt;
+         *         &amp;lt;element name="attributeName" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="2" minOccurs="0"/&amp;gt;
+         *         &amp;lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet" minOccurs="0"/&amp;gt;
+         *       &amp;lt;/sequence&amp;gt;
+         *       &amp;lt;attribute name="type" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *       &amp;lt;attribute name="colour" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+         *       &amp;lt;attribute name="display" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+         *       &amp;lt;attribute name="order" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+         *       &amp;lt;attribute name="mincolour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+         *       &amp;lt;attribute name="noValueColour" type="{www.jalview.org/colours}NoValueColour" default="Min" /&amp;gt;
+         *       &amp;lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+         *       &amp;lt;attribute name="threshstate" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+         *       &amp;lt;attribute name="max" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+         *       &amp;lt;attribute name="min" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+         *       &amp;lt;attribute name="colourByLabel" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+         *       &amp;lt;attribute name="autoScale" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+         *     &amp;lt;/restriction&amp;gt;
+         *   &amp;lt;/complexContent&amp;gt;
+         * &amp;lt;/complexType&amp;gt;
+         * &lt;/pre&gt;
          * 
          * 
          */
@@ -933,20 +933,20 @@ public class JalviewModel {
             /**
              * Gets the value of the attributeName property.
              * 
-             * <p>
+             * &lt;p&gt;
              * 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.
+             * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the attributeName property.
              * 
-             * <p>
+             * &lt;p&gt;
              * For example, to add a new item, do as follows:
-             * <pre>
+             * &lt;pre&gt;
              *    getAttributeName().add(newItem);
-             * </pre>
+             * &lt;/pre&gt;
              * 
              * 
-             * <p>
+             * &lt;p&gt;
              * Objects of the following type(s) are allowed in the list
              * {@link String }
              * 
@@ -1265,41 +1265,41 @@ public class JalviewModel {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element name="seq" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded"/&amp;gt;
+     *         &amp;lt;element name="annotationColours" type="{www.jalview.org}AnnotationColourScheme" minOccurs="0"/&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attribute name="start" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="end" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="name" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="consThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="pidThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="outlineColour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="displayBoxes" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="displayText" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="colourText" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="textCol1" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="textCol2" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="textColThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="showUnconserved" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="ignoreGapsinConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+     *       &amp;lt;attribute name="showConsensusHistogram" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+     *       &amp;lt;attribute name="showSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *       &amp;lt;attribute name="normaliseSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *       &amp;lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -1356,20 +1356,20 @@ public class JalviewModel {
         /**
          * Gets the value of the seq property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the seq property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getSeq().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link String }
          * 
@@ -1882,80 +1882,80 @@ public class JalviewModel {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element name="features" type="{www.jalview.org}feature" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+     *         &amp;lt;element name="pdbids" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+     *           &amp;lt;complexType&amp;gt;
+     *             &amp;lt;complexContent&amp;gt;
+     *               &amp;lt;extension base="{www.jalview.org}pdbentry"&amp;gt;
+     *                 &amp;lt;sequence&amp;gt;
+     *                   &amp;lt;element name="structureState" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+     *                     &amp;lt;complexType&amp;gt;
+     *                       &amp;lt;simpleContent&amp;gt;
+     *                         &amp;lt;extension base="&amp;lt;http://www.w3.org/2001/XMLSchema&amp;gt;string"&amp;gt;
+     *                           &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+     *                           &amp;lt;attribute name="visible" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *                           &amp;lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *                           &amp;lt;attribute name="alignwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+     *                           &amp;lt;attribute name="colourwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *                           &amp;lt;attribute name="colourByJmol" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+     *                           &amp;lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *                         &amp;lt;/extension&amp;gt;
+     *                       &amp;lt;/simpleContent&amp;gt;
+     *                     &amp;lt;/complexType&amp;gt;
+     *                   &amp;lt;/element&amp;gt;
+     *                 &amp;lt;/sequence&amp;gt;
+     *               &amp;lt;/extension&amp;gt;
+     *             &amp;lt;/complexContent&amp;gt;
+     *           &amp;lt;/complexType&amp;gt;
+     *         &amp;lt;/element&amp;gt;
+     *         &amp;lt;element name="hiddenSequences" type="{http://www.w3.org/2001/XMLSchema}int" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+     *         &amp;lt;element name="rnaViewer" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+     *           &amp;lt;complexType&amp;gt;
+     *             &amp;lt;complexContent&amp;gt;
+     *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *                 &amp;lt;sequence&amp;gt;
+     *                   &amp;lt;element name="secondaryStructure" maxOccurs="unbounded"&amp;gt;
+     *                     &amp;lt;complexType&amp;gt;
+     *                       &amp;lt;complexContent&amp;gt;
+     *                         &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *                           &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *                           &amp;lt;attribute name="annotationId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *                           &amp;lt;attribute name="gapped" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *                           &amp;lt;attribute name="viewerState" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *                         &amp;lt;/restriction&amp;gt;
+     *                       &amp;lt;/complexContent&amp;gt;
+     *                     &amp;lt;/complexType&amp;gt;
+     *                   &amp;lt;/element&amp;gt;
+     *                 &amp;lt;/sequence&amp;gt;
+     *                 &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+     *                 &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *                 &amp;lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *                 &amp;lt;attribute name="dividerLocation" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *                 &amp;lt;attribute name="selectedRna" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *               &amp;lt;/restriction&amp;gt;
+     *             &amp;lt;/complexContent&amp;gt;
+     *           &amp;lt;/complexType&amp;gt;
+     *         &amp;lt;/element&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attribute name="colour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="start" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="end" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="hidden" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="viewreference" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -1992,20 +1992,20 @@ public class JalviewModel {
         /**
          * Gets the value of the features property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the features property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getFeatures().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link Feature }
          * 
@@ -2021,20 +2021,20 @@ public class JalviewModel {
         /**
          * Gets the value of the pdbids property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the pdbids property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getPdbids().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link JalviewModel.JSeq.Pdbids }
          * 
@@ -2050,20 +2050,20 @@ public class JalviewModel {
         /**
          * Gets the value of the hiddenSequences property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the hiddenSequences property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getHiddenSequences().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link Integer }
          * 
@@ -2079,20 +2079,20 @@ public class JalviewModel {
         /**
          * Gets the value of the rnaViewer property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the rnaViewer property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getRnaViewer().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link JalviewModel.JSeq.RnaViewer }
          * 
@@ -2235,35 +2235,35 @@ public class JalviewModel {
 
 
         /**
-         * <p>Java class for anonymous complex type.
+         * &lt;p&gt;Java class for anonymous complex type.
          * 
-         * <p>The following schema fragment specifies the expected content contained within this class.
+         * &lt;p&gt;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>
+         * &lt;pre&gt;
+         * &amp;lt;complexType&amp;gt;
+         *   &amp;lt;complexContent&amp;gt;
+         *     &amp;lt;extension base="{www.jalview.org}pdbentry"&amp;gt;
+         *       &amp;lt;sequence&amp;gt;
+         *         &amp;lt;element name="structureState" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+         *           &amp;lt;complexType&amp;gt;
+         *             &amp;lt;simpleContent&amp;gt;
+         *               &amp;lt;extension base="&amp;lt;http://www.w3.org/2001/XMLSchema&amp;gt;string"&amp;gt;
+         *                 &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+         *                 &amp;lt;attribute name="visible" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+         *                 &amp;lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *                 &amp;lt;attribute name="alignwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+         *                 &amp;lt;attribute name="colourwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+         *                 &amp;lt;attribute name="colourByJmol" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+         *                 &amp;lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *               &amp;lt;/extension&amp;gt;
+         *             &amp;lt;/simpleContent&amp;gt;
+         *           &amp;lt;/complexType&amp;gt;
+         *         &amp;lt;/element&amp;gt;
+         *       &amp;lt;/sequence&amp;gt;
+         *     &amp;lt;/extension&amp;gt;
+         *   &amp;lt;/complexContent&amp;gt;
+         * &amp;lt;/complexType&amp;gt;
+         * &lt;/pre&gt;
          * 
          * 
          */
@@ -2281,20 +2281,20 @@ public class JalviewModel {
             /**
              * Gets the value of the structureState property.
              * 
-             * <p>
+             * &lt;p&gt;
              * 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.
+             * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the structureState property.
              * 
-             * <p>
+             * &lt;p&gt;
              * For example, to add a new item, do as follows:
-             * <pre>
+             * &lt;pre&gt;
              *    getStructureState().add(newItem);
-             * </pre>
+             * &lt;/pre&gt;
              * 
              * 
-             * <p>
+             * &lt;p&gt;
              * Objects of the following type(s) are allowed in the list
              * {@link JalviewModel.JSeq.Pdbids.StructureState }
              * 
@@ -2309,25 +2309,25 @@ public class JalviewModel {
 
 
             /**
-             * <p>Java class for anonymous complex type.
+             * &lt;p&gt;Java class for anonymous complex type.
              * 
-             * <p>The following schema fragment specifies the expected content contained within this class.
+             * &lt;p&gt;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>
+             * &lt;pre&gt;
+             * &amp;lt;complexType&amp;gt;
+             *   &amp;lt;simpleContent&amp;gt;
+             *     &amp;lt;extension base="&amp;lt;http://www.w3.org/2001/XMLSchema&amp;gt;string"&amp;gt;
+             *       &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+             *       &amp;lt;attribute name="visible" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+             *       &amp;lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+             *       &amp;lt;attribute name="alignwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+             *       &amp;lt;attribute name="colourwithAlignPanel" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+             *       &amp;lt;attribute name="colourByJmol" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+             *       &amp;lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+             *     &amp;lt;/extension&amp;gt;
+             *   &amp;lt;/simpleContent&amp;gt;
+             * &amp;lt;/complexType&amp;gt;
+             * &lt;/pre&gt;
              * 
              * 
              */
@@ -2642,37 +2642,37 @@ public class JalviewModel {
 
 
         /**
-         * <p>Java class for anonymous complex type.
+         * &lt;p&gt;Java class for anonymous complex type.
          * 
-         * <p>The following schema fragment specifies the expected content contained within this class.
+         * &lt;p&gt;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>
+         * &lt;pre&gt;
+         * &amp;lt;complexType&amp;gt;
+         *   &amp;lt;complexContent&amp;gt;
+         *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+         *       &amp;lt;sequence&amp;gt;
+         *         &amp;lt;element name="secondaryStructure" maxOccurs="unbounded"&amp;gt;
+         *           &amp;lt;complexType&amp;gt;
+         *             &amp;lt;complexContent&amp;gt;
+         *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+         *                 &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *                 &amp;lt;attribute name="annotationId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *                 &amp;lt;attribute name="gapped" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+         *                 &amp;lt;attribute name="viewerState" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *               &amp;lt;/restriction&amp;gt;
+         *             &amp;lt;/complexContent&amp;gt;
+         *           &amp;lt;/complexType&amp;gt;
+         *         &amp;lt;/element&amp;gt;
+         *       &amp;lt;/sequence&amp;gt;
+         *       &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+         *       &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *       &amp;lt;attribute name="viewId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *       &amp;lt;attribute name="dividerLocation" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+         *       &amp;lt;attribute name="selectedRna" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+         *     &amp;lt;/restriction&amp;gt;
+         *   &amp;lt;/complexContent&amp;gt;
+         * &amp;lt;/complexType&amp;gt;
+         * &lt;/pre&gt;
          * 
          * 
          */
@@ -2704,20 +2704,20 @@ public class JalviewModel {
             /**
              * Gets the value of the secondaryStructure property.
              * 
-             * <p>
+             * &lt;p&gt;
              * 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.
+             * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the secondaryStructure property.
              * 
-             * <p>
+             * &lt;p&gt;
              * For example, to add a new item, do as follows:
-             * <pre>
+             * &lt;pre&gt;
              *    getSecondaryStructure().add(newItem);
-             * </pre>
+             * &lt;/pre&gt;
              * 
              * 
-             * <p>
+             * &lt;p&gt;
              * Objects of the following type(s) are allowed in the list
              * {@link JalviewModel.JSeq.RnaViewer.SecondaryStructure }
              * 
@@ -2924,22 +2924,22 @@ public class JalviewModel {
 
 
             /**
-             * <p>Java class for anonymous complex type.
+             * &lt;p&gt;Java class for anonymous complex type.
              * 
-             * <p>The following schema fragment specifies the expected content contained within this class.
+             * &lt;p&gt;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>
+             * &lt;pre&gt;
+             * &amp;lt;complexType&amp;gt;
+             *   &amp;lt;complexContent&amp;gt;
+             *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+             *       &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+             *       &amp;lt;attribute name="annotationId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+             *       &amp;lt;attribute name="gapped" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+             *       &amp;lt;attribute name="viewerState" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+             *     &amp;lt;/restriction&amp;gt;
+             *   &amp;lt;/complexContent&amp;gt;
+             * &amp;lt;/complexType&amp;gt;
+             * &lt;/pre&gt;
              * 
              * 
              */
@@ -3060,69 +3060,69 @@ public class JalviewModel {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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="sequencePoint" maxOccurs="unbounded">
-     *           &lt;complexType>
-     *             &lt;complexContent>
-     *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-     *                 &lt;attGroup ref="{www.jalview.org}position"/>
-     *                 &lt;attribute name="sequenceRef" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *               &lt;/restriction>
-     *             &lt;/complexContent>
-     *           &lt;/complexType>
-     *         &lt;/element>
-     *         &lt;element name="axis" maxOccurs="3" minOccurs="3">
-     *           &lt;complexType>
-     *             &lt;complexContent>
-     *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-     *                 &lt;attGroup ref="{www.jalview.org}position"/>
-     *               &lt;/restriction>
-     *             &lt;/complexContent>
-     *           &lt;/complexType>
-     *         &lt;/element>
-     *         &lt;element name="seqPointMin">
-     *           &lt;complexType>
-     *             &lt;complexContent>
-     *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-     *                 &lt;attGroup ref="{www.jalview.org}position"/>
-     *               &lt;/restriction>
-     *             &lt;/complexContent>
-     *           &lt;/complexType>
-     *         &lt;/element>
-     *         &lt;element name="seqPointMax">
-     *           &lt;complexType>
-     *             &lt;complexContent>
-     *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-     *                 &lt;attGroup ref="{www.jalview.org}position"/>
-     *               &lt;/restriction>
-     *             &lt;/complexContent>
-     *           &lt;/complexType>
-     *         &lt;/element>
-     *         &lt;element name="pcaData" type="{www.jalview.org}PcaDataType"/>
-     *       &lt;/sequence>
-     *       &lt;attGroup ref="{www.jalview.org}swingwindow"/>
-     *       &lt;attGroup ref="{www.jalview.org}SimilarityParams"/>
-     *       &lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *       &lt;attribute name="scoreModelName" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *       &lt;attribute name="xDim" type="{http://www.w3.org/2001/XMLSchema}int" />
-     *       &lt;attribute name="yDim" type="{http://www.w3.org/2001/XMLSchema}int" />
-     *       &lt;attribute name="zDim" type="{http://www.w3.org/2001/XMLSchema}int" />
-     *       &lt;attribute name="bgColour" type="{http://www.w3.org/2001/XMLSchema}int" />
-     *       &lt;attribute name="scaleFactor" type="{http://www.w3.org/2001/XMLSchema}float" />
-     *       &lt;attribute name="showLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" />
-     *       &lt;attribute name="linkToAllViews" type="{http://www.w3.org/2001/XMLSchema}boolean" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element name="sequencePoint" maxOccurs="unbounded"&amp;gt;
+     *           &amp;lt;complexType&amp;gt;
+     *             &amp;lt;complexContent&amp;gt;
+     *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *                 &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+     *                 &amp;lt;attribute name="sequenceRef" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *               &amp;lt;/restriction&amp;gt;
+     *             &amp;lt;/complexContent&amp;gt;
+     *           &amp;lt;/complexType&amp;gt;
+     *         &amp;lt;/element&amp;gt;
+     *         &amp;lt;element name="axis" maxOccurs="3" minOccurs="3"&amp;gt;
+     *           &amp;lt;complexType&amp;gt;
+     *             &amp;lt;complexContent&amp;gt;
+     *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *                 &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+     *               &amp;lt;/restriction&amp;gt;
+     *             &amp;lt;/complexContent&amp;gt;
+     *           &amp;lt;/complexType&amp;gt;
+     *         &amp;lt;/element&amp;gt;
+     *         &amp;lt;element name="seqPointMin"&amp;gt;
+     *           &amp;lt;complexType&amp;gt;
+     *             &amp;lt;complexContent&amp;gt;
+     *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *                 &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+     *               &amp;lt;/restriction&amp;gt;
+     *             &amp;lt;/complexContent&amp;gt;
+     *           &amp;lt;/complexType&amp;gt;
+     *         &amp;lt;/element&amp;gt;
+     *         &amp;lt;element name="seqPointMax"&amp;gt;
+     *           &amp;lt;complexType&amp;gt;
+     *             &amp;lt;complexContent&amp;gt;
+     *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *                 &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+     *               &amp;lt;/restriction&amp;gt;
+     *             &amp;lt;/complexContent&amp;gt;
+     *           &amp;lt;/complexType&amp;gt;
+     *         &amp;lt;/element&amp;gt;
+     *         &amp;lt;element name="pcaData" type="{www.jalview.org}PcaDataType"/&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+     *       &amp;lt;attGroup ref="{www.jalview.org}SimilarityParams"/&amp;gt;
+     *       &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="scoreModelName" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="xDim" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="yDim" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="zDim" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="bgColour" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="scaleFactor" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+     *       &amp;lt;attribute name="showLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="linkToAllViews" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -3184,20 +3184,20 @@ public class JalviewModel {
         /**
          * Gets the value of the sequencePoint property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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 sequencePoint property.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the sequencePoint property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getSequencePoint().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link JalviewModel.PcaViewer.SequencePoint }
          * 
@@ -3213,20 +3213,20 @@ public class JalviewModel {
         /**
          * Gets the value of the axis property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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 axis property.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the axis property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getAxis().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link JalviewModel.PcaViewer.Axis }
          * 
@@ -3721,19 +3721,19 @@ public class JalviewModel {
 
 
         /**
-         * <p>Java class for anonymous complex type.
+         * &lt;p&gt;Java class for anonymous complex type.
          * 
-         * <p>The following schema fragment specifies the expected content contained within this class.
+         * &lt;p&gt;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;attGroup ref="{www.jalview.org}position"/>
-         *     &lt;/restriction>
-         *   &lt;/complexContent>
-         * &lt;/complexType>
-         * </pre>
+         * &lt;pre&gt;
+         * &amp;lt;complexType&amp;gt;
+         *   &amp;lt;complexContent&amp;gt;
+         *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+         *       &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+         *     &amp;lt;/restriction&amp;gt;
+         *   &amp;lt;/complexContent&amp;gt;
+         * &amp;lt;/complexType&amp;gt;
+         * &lt;/pre&gt;
          * 
          * 
          */
@@ -3824,19 +3824,19 @@ public class JalviewModel {
 
 
         /**
-         * <p>Java class for anonymous complex type.
+         * &lt;p&gt;Java class for anonymous complex type.
          * 
-         * <p>The following schema fragment specifies the expected content contained within this class.
+         * &lt;p&gt;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;attGroup ref="{www.jalview.org}position"/>
-         *     &lt;/restriction>
-         *   &lt;/complexContent>
-         * &lt;/complexType>
-         * </pre>
+         * &lt;pre&gt;
+         * &amp;lt;complexType&amp;gt;
+         *   &amp;lt;complexContent&amp;gt;
+         *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+         *       &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+         *     &amp;lt;/restriction&amp;gt;
+         *   &amp;lt;/complexContent&amp;gt;
+         * &amp;lt;/complexType&amp;gt;
+         * &lt;/pre&gt;
          * 
          * 
          */
@@ -3927,19 +3927,19 @@ public class JalviewModel {
 
 
         /**
-         * <p>Java class for anonymous complex type.
+         * &lt;p&gt;Java class for anonymous complex type.
          * 
-         * <p>The following schema fragment specifies the expected content contained within this class.
+         * &lt;p&gt;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;attGroup ref="{www.jalview.org}position"/>
-         *     &lt;/restriction>
-         *   &lt;/complexContent>
-         * &lt;/complexType>
-         * </pre>
+         * &lt;pre&gt;
+         * &amp;lt;complexType&amp;gt;
+         *   &amp;lt;complexContent&amp;gt;
+         *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+         *       &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+         *     &amp;lt;/restriction&amp;gt;
+         *   &amp;lt;/complexContent&amp;gt;
+         * &amp;lt;/complexType&amp;gt;
+         * &lt;/pre&gt;
          * 
          * 
          */
@@ -4030,20 +4030,20 @@ public class JalviewModel {
 
 
         /**
-         * <p>Java class for anonymous complex type.
+         * &lt;p&gt;Java class for anonymous complex type.
          * 
-         * <p>The following schema fragment specifies the expected content contained within this class.
+         * &lt;p&gt;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;attGroup ref="{www.jalview.org}position"/>
-         *       &lt;attribute name="sequenceRef" type="{http://www.w3.org/2001/XMLSchema}string" />
-         *     &lt;/restriction>
-         *   &lt;/complexContent>
-         * &lt;/complexType>
-         * </pre>
+         * &lt;pre&gt;
+         * &amp;lt;complexType&amp;gt;
+         *   &amp;lt;complexContent&amp;gt;
+         *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+         *       &amp;lt;attGroup ref="{www.jalview.org}position"/&amp;gt;
+         *       &amp;lt;attribute name="sequenceRef" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *     &amp;lt;/restriction&amp;gt;
+         *   &amp;lt;/complexContent&amp;gt;
+         * &amp;lt;/complexType&amp;gt;
+         * &lt;/pre&gt;
          * 
          * 
          */
@@ -4162,34 +4162,34 @@ public class JalviewModel {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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;attribute name="linkToAllViews" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence minOccurs="0"&amp;gt;
+     *         &amp;lt;element name="title" type="{http://www.w3.org/2001/XMLSchema}string"/&amp;gt;
+     *         &amp;lt;element name="newick" type="{http://www.w3.org/2001/XMLSchema}string"/&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+     *       &amp;lt;attribute name="fontName" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="fontSize" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="fontStyle" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+     *       &amp;lt;attribute name="showBootstrap" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="showDistances" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="markUnlinked" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="fitToWindow" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="currentTree" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" /&amp;gt;
+     *       &amp;lt;attribute name="linkToAllViews" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -4654,22 +4654,22 @@ public class JalviewModel {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element name="UserColourScheme" type="{www.jalview.org/colours}JalviewUserColours"/&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -4736,86 +4736,86 @@ public class JalviewModel {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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;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>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element name="AnnotationColours" type="{www.jalview.org}AnnotationColourScheme" minOccurs="0"/&amp;gt;
+     *         &amp;lt;element name="hiddenColumns" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+     *           &amp;lt;complexType&amp;gt;
+     *             &amp;lt;complexContent&amp;gt;
+     *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *                 &amp;lt;attribute name="start" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *                 &amp;lt;attribute name="end" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *               &amp;lt;/restriction&amp;gt;
+     *             &amp;lt;/complexContent&amp;gt;
+     *           &amp;lt;/complexType&amp;gt;
+     *         &amp;lt;/element&amp;gt;
+     *         &amp;lt;element name="calcIdParam" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+     *           &amp;lt;complexType&amp;gt;
+     *             &amp;lt;complexContent&amp;gt;
+     *               &amp;lt;extension base="{www.jalview.org/xml/wsparamset}WebServiceParameterSet"&amp;gt;
+     *                 &amp;lt;attribute name="calcId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *                 &amp;lt;attribute name="needsUpdate" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *                 &amp;lt;attribute name="autoUpdate" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *               &amp;lt;/extension&amp;gt;
+     *             &amp;lt;/complexContent&amp;gt;
+     *           &amp;lt;/complexType&amp;gt;
+     *         &amp;lt;/element&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attGroup ref="{www.jalview.org}swingwindow"/&amp;gt;
+     *       &amp;lt;attribute name="conservationSelected" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="pidSelected" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="bgColour" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="consThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="pidThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="title" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="showFullId" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="rightAlignIds" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="showText" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="showColourText" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="showUnconserved" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *       &amp;lt;attribute name="showBoxes" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="wrapAlignment" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="renderGaps" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="showSequenceFeatures" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="showNPfeatureTooltip" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="showDbRefTooltip" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="followHighlight" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+     *       &amp;lt;attribute name="followSelection" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+     *       &amp;lt;attribute name="showAnnotation" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="centreColumnLabels" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *       &amp;lt;attribute name="showGroupConservation" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *       &amp;lt;attribute name="showGroupConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *       &amp;lt;attribute name="showConsensusHistogram" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+     *       &amp;lt;attribute name="showSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *       &amp;lt;attribute name="normaliseSequenceLogo" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *       &amp;lt;attribute name="ignoreGapsinConsensus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+     *       &amp;lt;attribute name="startRes" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="startSeq" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="fontName" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="fontSize" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="fontStyle" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="scaleProteinAsCdna" type="{http://www.w3.org/2001/XMLSchema}boolean" default="true" /&amp;gt;
+     *       &amp;lt;attribute name="viewName" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="sequenceSetId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="gatheredViews" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="textCol1" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="textCol2" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="textColThreshold" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}ID" /&amp;gt;
+     *       &amp;lt;attribute name="complementId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="showComplementFeatures" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *       &amp;lt;attribute name="showComplementFeaturesOnTop" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -4958,20 +4958,20 @@ public class JalviewModel {
         /**
          * Gets the value of the hiddenColumns property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the hiddenColumns property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getHiddenColumns().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link JalviewModel.Viewport.HiddenColumns }
          * 
@@ -4987,20 +4987,20 @@ public class JalviewModel {
         /**
          * Gets the value of the calcIdParam property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the calcIdParam property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getCalcIdParam().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link JalviewModel.Viewport.CalcIdParam }
          * 
@@ -6195,21 +6195,21 @@ public class JalviewModel {
 
 
         /**
-         * <p>Java class for anonymous complex type.
+         * &lt;p&gt;Java class for anonymous complex type.
          * 
-         * <p>The following schema fragment specifies the expected content contained within this class.
+         * &lt;p&gt;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>
+         * &lt;pre&gt;
+         * &amp;lt;complexType&amp;gt;
+         *   &amp;lt;complexContent&amp;gt;
+         *     &amp;lt;extension base="{www.jalview.org/xml/wsparamset}WebServiceParameterSet"&amp;gt;
+         *       &amp;lt;attribute name="calcId" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+         *       &amp;lt;attribute name="needsUpdate" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+         *       &amp;lt;attribute name="autoUpdate" use="required" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+         *     &amp;lt;/extension&amp;gt;
+         *   &amp;lt;/complexContent&amp;gt;
+         * &amp;lt;/complexType&amp;gt;
+         * &lt;/pre&gt;
          * 
          * 
          */
@@ -6298,20 +6298,20 @@ public class JalviewModel {
 
 
         /**
-         * <p>Java class for anonymous complex type.
+         * &lt;p&gt;Java class for anonymous complex type.
          * 
-         * <p>The following schema fragment specifies the expected content contained within this class.
+         * &lt;p&gt;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>
+         * &lt;pre&gt;
+         * &amp;lt;complexType&amp;gt;
+         *   &amp;lt;complexContent&amp;gt;
+         *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+         *       &amp;lt;attribute name="start" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+         *       &amp;lt;attribute name="end" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+         *     &amp;lt;/restriction&amp;gt;
+         *   &amp;lt;/complexContent&amp;gt;
+         * &amp;lt;/complexType&amp;gt;
+         * &lt;/pre&gt;
          * 
          * 
          */
index c43e04c..e1ee27e 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -18,55 +18,55 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for JalviewUserColours complex type.
+ * &lt;p&gt;Java class for JalviewUserColours complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="JalviewUserColours">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;sequence>
- *         &lt;element name="Version" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
- *         &lt;element name="colour" 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;/sequence>
- *                 &lt;attribute name="Name" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                 &lt;attribute name="RGB" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                 &lt;attribute name="minRGB" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                 &lt;attribute name="noValueColour" type="{www.jalview.org/colours}NoValueColour" default="Min" />
- *                 &lt;attribute name="threshType" type="{www.jalview.org/colours}ThresholdType" />
- *                 &lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" />
- *                 &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="filter" maxOccurs="unbounded" minOccurs="0">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &lt;sequence>
- *                   &lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet"/>
- *                 &lt;/sequence>
- *                 &lt;attribute name="featureType" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
- *               &lt;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *       &lt;/sequence>
- *       &lt;attribute name="schemeName" type="{http://www.w3.org/2001/XMLSchema}string" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="JalviewUserColours"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="Version" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="colour" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;sequence&amp;gt;
+ *                   &amp;lt;element name="attributeName" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="2" minOccurs="0"/&amp;gt;
+ *                 &amp;lt;/sequence&amp;gt;
+ *                 &amp;lt;attribute name="Name" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="RGB" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="minRGB" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="noValueColour" type="{www.jalview.org/colours}NoValueColour" default="Min" /&amp;gt;
+ *                 &amp;lt;attribute name="threshType" type="{www.jalview.org/colours}ThresholdType" /&amp;gt;
+ *                 &amp;lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *                 &amp;lt;attribute name="max" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *                 &amp;lt;attribute name="min" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+ *                 &amp;lt;attribute name="colourByLabel" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *                 &amp;lt;attribute name="autoScale" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *         &amp;lt;element name="filter" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;sequence&amp;gt;
+ *                   &amp;lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet"/&amp;gt;
+ *                 &amp;lt;/sequence&amp;gt;
+ *                 &amp;lt;attribute name="featureType" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="schemeName" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -114,20 +114,20 @@ public class JalviewUserColours {
     /**
      * Gets the value of the colour property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 colour property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the colour property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getColour().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link JalviewUserColours.Colour }
      * 
@@ -143,20 +143,20 @@ public class JalviewUserColours {
     /**
      * Gets the value of the filter property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 filter property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the filter property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getFilter().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link JalviewUserColours.Filter }
      * 
@@ -195,31 +195,31 @@ public class JalviewUserColours {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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;/sequence>
-     *       &lt;attribute name="Name" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *       &lt;attribute name="RGB" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *       &lt;attribute name="minRGB" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *       &lt;attribute name="noValueColour" type="{www.jalview.org/colours}NoValueColour" default="Min" />
-     *       &lt;attribute name="threshType" type="{www.jalview.org/colours}ThresholdType" />
-     *       &lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" />
-     *       &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>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element name="attributeName" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="2" minOccurs="0"/&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attribute name="Name" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="RGB" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="minRGB" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="noValueColour" type="{www.jalview.org/colours}NoValueColour" default="Min" /&amp;gt;
+     *       &amp;lt;attribute name="threshType" type="{www.jalview.org/colours}ThresholdType" /&amp;gt;
+     *       &amp;lt;attribute name="threshold" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+     *       &amp;lt;attribute name="max" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+     *       &amp;lt;attribute name="min" type="{http://www.w3.org/2001/XMLSchema}float" /&amp;gt;
+     *       &amp;lt;attribute name="colourByLabel" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *       &amp;lt;attribute name="autoScale" type="{http://www.w3.org/2001/XMLSchema}boolean" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -255,20 +255,20 @@ public class JalviewUserColours {
         /**
          * Gets the value of the attributeName property.
          * 
-         * <p>
+         * &lt;p&gt;
          * 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.
+         * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the attributeName property.
          * 
-         * <p>
+         * &lt;p&gt;
          * For example, to add a new item, do as follows:
-         * <pre>
+         * &lt;pre&gt;
          *    getAttributeName().add(newItem);
-         * </pre>
+         * &lt;/pre&gt;
          * 
          * 
-         * <p>
+         * &lt;p&gt;
          * Objects of the following type(s) are allowed in the list
          * {@link String }
          * 
@@ -529,22 +529,22 @@ public class JalviewUserColours {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet"/>
-     *       &lt;/sequence>
-     *       &lt;attribute name="featureType" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element name="matcherSet" type="{www.jalview.org/colours}FeatureMatcherSet"/&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attribute name="featureType" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
index 1a31d82..c93112c 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -24,42 +24,42 @@ import javax.xml.bind.annotation.XmlType;
  *                             This effectively represents a java.util.MapList object
  *                     
  * 
- * <p>Java class for mapListType complex type.
+ * &lt;p&gt;Java class for mapListType complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="mapListType">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;sequence>
- *         &lt;element name="mapListFrom" maxOccurs="unbounded" minOccurs="0">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &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;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *         &lt;element name="mapListTo" maxOccurs="unbounded" minOccurs="0">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &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;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *       &lt;/sequence>
- *       &lt;attribute name="mapFromUnit" use="required" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
- *       &lt;attribute name="mapToUnit" use="required" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="mapListType"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="mapListFrom" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;attribute name="start" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                 &amp;lt;attribute name="end" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *         &amp;lt;element name="mapListTo" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;attribute name="start" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *                 &amp;lt;attribute name="end" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="mapFromUnit" use="required" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" /&amp;gt;
+ *       &amp;lt;attribute name="mapToUnit" use="required" type="{http://www.w3.org/2001/XMLSchema}positiveInteger" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -85,20 +85,20 @@ public class MapListType {
     /**
      * Gets the value of the mapListFrom property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 mapListFrom property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the mapListFrom property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getMapListFrom().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link MapListType.MapListFrom }
      * 
@@ -114,20 +114,20 @@ public class MapListType {
     /**
      * Gets the value of the mapListTo property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 mapListTo property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the mapListTo property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getMapListTo().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link MapListType.MapListTo }
      * 
@@ -190,20 +190,20 @@ public class MapListType {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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" 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;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;attribute name="start" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="end" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -252,20 +252,20 @@ public class MapListType {
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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" 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;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;attribute name="start" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *       &amp;lt;attribute name="end" use="required" type="{http://www.w3.org/2001/XMLSchema}int" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
index 5ebeb7e..24f5b01 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -23,30 +23,30 @@ import javax.xml.bind.annotation.XmlType;
  *                                     to the sequence set (which will mean they are then added to the alignment too).
  *                             
  * 
- * <p>Java class for anonymous complex type.
+ * &lt;p&gt;Java class for anonymous complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType>
- *   &lt;complexContent>
- *     &lt;extension base="{www.vamsas.ac.uk/jalview/version2}mapListType">
- *       &lt;sequence>
- *         &lt;choice minOccurs="0">
- *           &lt;element ref="{www.vamsas.ac.uk/jalview/version2}Sequence"/>
- *           &lt;element name="dseqFor">
- *             &lt;simpleType>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string">
- *               &lt;/restriction>
- *             &lt;/simpleType>
- *           &lt;/element>
- *         &lt;/choice>
- *       &lt;/sequence>
- *       &lt;attribute name="mappingType" type="{http://www.w3.org/2001/XMLSchema}string" />
- *     &lt;/extension>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;extension base="{www.vamsas.ac.uk/jalview/version2}mapListType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;choice minOccurs="0"&amp;gt;
+ *           &amp;lt;element ref="{www.vamsas.ac.uk/jalview/version2}Sequence"/&amp;gt;
+ *           &amp;lt;element name="dseqFor"&amp;gt;
+ *             &amp;lt;simpleType&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}string"&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/simpleType&amp;gt;
+ *           &amp;lt;/element&amp;gt;
+ *         &amp;lt;/choice&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="mappingType" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *     &amp;lt;/extension&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
index 9db4ea3..7e42db8 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -14,19 +14,18 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for NoValueColour.
+ * &lt;p&gt;Java class for NoValueColour.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
- * <p>
- * <pre>
- * &lt;simpleType name="NoValueColour">
- *   &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string">
- *     &lt;enumeration value="None"/>
- *     &lt;enumeration value="Min"/>
- *     &lt;enumeration value="Max"/>
- *   &lt;/restriction>
- * &lt;/simpleType>
- * </pre>
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
+ * &lt;pre&gt;
+ * &amp;lt;simpleType name="NoValueColour"&amp;gt;
+ *   &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}string"&amp;gt;
+ *     &amp;lt;enumeration value="None"/&amp;gt;
+ *     &amp;lt;enumeration value="Min"/&amp;gt;
+ *     &amp;lt;enumeration value="Max"/&amp;gt;
+ *   &amp;lt;/restriction&amp;gt;
+ * &amp;lt;/simpleType&amp;gt;
+ * &lt;/pre&gt;
  * 
  */
 @XmlType(name = "NoValueColour", namespace = "www.jalview.org/colours")
index e0b2127..b6ea489 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -18,7 +18,7 @@ import javax.xml.namespace.QName;
  * This object contains factory methods for each 
  * Java content interface and Java element interface 
  * generated in the jalview.xml.binding.jalview package. 
- * <p>An ObjectFactory allows you to programatically 
+ * &lt;p&gt;An ObjectFactory allows you to programatically 
  * construct new instances of the Java representation 
  * for XML content. The Java representation of XML 
  * content can consist of schema derived interfaces 
@@ -31,9 +31,9 @@ import javax.xml.namespace.QName;
 @XmlRegistry
 public class ObjectFactory {
 
-    private final static QName _WebServiceParameterSet_QNAME = new QName("www.jalview.org/xml/wsparamset", "WebServiceParameterSet");
     private final static QName _JalviewModel_QNAME = new QName("www.jalview.org", "JalviewModel");
     private final static QName _JalviewUserColours_QNAME = new QName("www.jalview.org/colours", "JalviewUserColours");
+    private final static QName _WebServiceParameterSet_QNAME = new QName("www.jalview.org/xml/wsparamset", "WebServiceParameterSet");
 
     /**
      * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: jalview.xml.binding.jalview
@@ -43,14 +43,6 @@ public class ObjectFactory {
     }
 
     /**
-     * Create an instance of {@link AlcodonFrame }
-     * 
-     */
-    public AlcodonFrame createAlcodonFrame() {
-        return new AlcodonFrame();
-    }
-
-    /**
      * Create an instance of {@link MapListType }
      * 
      */
@@ -67,6 +59,14 @@ public class ObjectFactory {
     }
 
     /**
+     * Create an instance of {@link AlcodonFrame }
+     * 
+     */
+    public AlcodonFrame createAlcodonFrame() {
+        return new AlcodonFrame();
+    }
+
+    /**
      * Create an instance of {@link Annotation }
      * 
      */
@@ -171,14 +171,6 @@ public class ObjectFactory {
     }
 
     /**
-     * Create an instance of {@link DoubleMatrix }
-     * 
-     */
-    public DoubleMatrix createDoubleMatrix() {
-        return new DoubleMatrix();
-    }
-
-    /**
      * Create an instance of {@link AnnotationColourScheme }
      * 
      */
@@ -203,27 +195,11 @@ public class ObjectFactory {
     }
 
     /**
-     * Create an instance of {@link AlcodonFrame.Alcodon }
-     * 
-     */
-    public AlcodonFrame.Alcodon createAlcodonFrameAlcodon() {
-        return new AlcodonFrame.Alcodon();
-    }
-
-    /**
-     * Create an instance of {@link AlcodonFrame.AlcodMap }
-     * 
-     */
-    public AlcodonFrame.AlcodMap createAlcodonFrameAlcodMap() {
-        return new AlcodonFrame.AlcodMap();
-    }
-
-    /**
-     * Create an instance of {@link AnnotationElement }
+     * Create an instance of {@link DoubleMatrix }
      * 
      */
-    public AnnotationElement createAnnotationElement() {
-        return new AnnotationElement();
+    public DoubleMatrix createDoubleMatrix() {
+        return new DoubleMatrix();
     }
 
     /**
@@ -267,6 +243,30 @@ public class ObjectFactory {
     }
 
     /**
+     * Create an instance of {@link AlcodonFrame.Alcodon }
+     * 
+     */
+    public AlcodonFrame.Alcodon createAlcodonFrameAlcodon() {
+        return new AlcodonFrame.Alcodon();
+    }
+
+    /**
+     * Create an instance of {@link AlcodonFrame.AlcodMap }
+     * 
+     */
+    public AlcodonFrame.AlcodMap createAlcodonFrameAlcodMap() {
+        return new AlcodonFrame.AlcodMap();
+    }
+
+    /**
+     * Create an instance of {@link AnnotationElement }
+     * 
+     */
+    public AnnotationElement createAnnotationElement() {
+        return new AnnotationElement();
+    }
+
+    /**
      * Create an instance of {@link Annotation.ThresholdLine }
      * 
      */
@@ -459,17 +459,12 @@ public class ObjectFactory {
     }
 
     /**
-     * Create an instance of {@link JAXBElement }{@code <}{@link WebServiceParameterSet }{@code >}}
-     * 
-     */
-    @XmlElementDecl(namespace = "www.jalview.org/xml/wsparamset", name = "WebServiceParameterSet")
-    public JAXBElement<WebServiceParameterSet> createWebServiceParameterSet(WebServiceParameterSet value) {
-        return new JAXBElement<WebServiceParameterSet>(_WebServiceParameterSet_QNAME, WebServiceParameterSet.class, null, value);
-    }
-
-    /**
-     * Create an instance of {@link JAXBElement }{@code <}{@link JalviewModel }{@code >}}
+     * Create an instance of {@link JAXBElement }{@code <}{@link JalviewModel }{@code >}
      * 
+     * @param value
+     *     Java instance representing xml element's value.
+     * @return
+     *     the new instance of {@link JAXBElement }{@code <}{@link JalviewModel }{@code >}
      */
     @XmlElementDecl(namespace = "www.jalview.org", name = "JalviewModel")
     public JAXBElement<JalviewModel> createJalviewModel(JalviewModel value) {
@@ -477,12 +472,29 @@ public class ObjectFactory {
     }
 
     /**
-     * Create an instance of {@link JAXBElement }{@code <}{@link JalviewUserColours }{@code >}}
+     * Create an instance of {@link JAXBElement }{@code <}{@link JalviewUserColours }{@code >}
      * 
+     * @param value
+     *     Java instance representing xml element's value.
+     * @return
+     *     the new instance of {@link JAXBElement }{@code <}{@link JalviewUserColours }{@code >}
      */
     @XmlElementDecl(namespace = "www.jalview.org/colours", name = "JalviewUserColours")
     public JAXBElement<JalviewUserColours> createJalviewUserColours(JalviewUserColours value) {
         return new JAXBElement<JalviewUserColours>(_JalviewUserColours_QNAME, JalviewUserColours.class, null, value);
     }
 
+    /**
+     * Create an instance of {@link JAXBElement }{@code <}{@link WebServiceParameterSet }{@code >}
+     * 
+     * @param value
+     *     Java instance representing xml element's value.
+     * @return
+     *     the new instance of {@link JAXBElement }{@code <}{@link WebServiceParameterSet }{@code >}
+     */
+    @XmlElementDecl(namespace = "www.jalview.org/xml/wsparamset", name = "WebServiceParameterSet")
+    public JAXBElement<WebServiceParameterSet> createWebServiceParameterSet(WebServiceParameterSet value) {
+        return new JAXBElement<WebServiceParameterSet>(_WebServiceParameterSet_QNAME, WebServiceParameterSet.class, null, value);
+    }
+
 }
index 6234f32..717ce89 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -19,23 +19,23 @@ import javax.xml.bind.annotation.XmlType;
  *                             The results of a PCA calculation
  *                     
  * 
- * <p>Java class for PcaDataType complex type.
+ * &lt;p&gt;Java class for PcaDataType complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="PcaDataType">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;sequence>
- *         &lt;element name="pairwiseMatrix" type="{www.jalview.org}DoubleMatrix"/>
- *         &lt;element name="tridiagonalMatrix" type="{www.jalview.org}DoubleMatrix"/>
- *         &lt;element name="eigenMatrix" type="{www.jalview.org}DoubleMatrix"/>
- *       &lt;/sequence>
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="PcaDataType"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="pairwiseMatrix" type="{www.jalview.org}DoubleMatrix"/&amp;gt;
+ *         &amp;lt;element name="tridiagonalMatrix" type="{www.jalview.org}DoubleMatrix"/&amp;gt;
+ *         &amp;lt;element name="eigenMatrix" type="{www.jalview.org}DoubleMatrix"/&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
index d5132ab..6983c04 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -18,33 +18,33 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for pdbentry complex type.
+ * &lt;p&gt;Java class for pdbentry complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="pdbentry">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;sequence maxOccurs="unbounded" minOccurs="0">
- *         &lt;element name="property" 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="value" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
- *               &lt;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *       &lt;/sequence>
- *       &lt;attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="file" type="{http://www.w3.org/2001/XMLSchema}string" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="pdbentry"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *         &amp;lt;element name="property" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="value" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="id" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="type" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="file" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -68,20 +68,20 @@ public class Pdbentry {
     /**
      * Gets the value of the property property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 property property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the property property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getProperty().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link Pdbentry.Property }
      * 
@@ -168,20 +168,20 @@ public class Pdbentry {
 
 
     /**
-     * <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="value" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;p&gt;Java class for anonymous complex type.
+     * 
+     * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
+     * 
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="value" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
index b842947..5381c32 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -19,37 +19,38 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for anonymous complex type.
+ * &lt;p&gt;Java class for anonymous complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType>
- *   &lt;complexContent>
- *     &lt;extension base="{www.vamsas.ac.uk/jalview/version2}SequenceType">
- *       &lt;sequence>
- *         &lt;element name="DBRef" maxOccurs="unbounded" minOccurs="0">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &lt;sequence>
- *                   &lt;element ref="{www.vamsas.ac.uk/jalview/version2}Mapping" minOccurs="0"/>
- *                 &lt;/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>
- *         &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>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;extension base="{www.vamsas.ac.uk/jalview/version2}SequenceType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="DBRef" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;sequence&amp;gt;
+ *                   &amp;lt;element ref="{www.vamsas.ac.uk/jalview/version2}Mapping" minOccurs="0"/&amp;gt;
+ *                 &amp;lt;/sequence&amp;gt;
+ *                 &amp;lt;attribute name="source" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="version" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="accessionId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="locus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *                 &amp;lt;attribute name="canonical" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="dsseqid" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="biotype" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *     &amp;lt;/extension&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -72,20 +73,20 @@ public class Sequence
     /**
      * Gets the value of the dbRef property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 dbRef property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the dbRef property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getDBRef().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link Sequence.DBRef }
      * 
@@ -148,25 +149,26 @@ public class Sequence
 
 
     /**
-     * <p>Java class for anonymous complex type.
+     * &lt;p&gt;Java class for anonymous complex type.
      * 
-     * <p>The following schema fragment specifies the expected content contained within this class.
+     * &lt;p&gt;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 ref="{www.vamsas.ac.uk/jalview/version2}Mapping" minOccurs="0"/>
-     *       &lt;/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>
-     * </pre>
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;sequence&amp;gt;
+     *         &amp;lt;element ref="{www.vamsas.ac.uk/jalview/version2}Mapping" minOccurs="0"/&amp;gt;
+     *       &amp;lt;/sequence&amp;gt;
+     *       &amp;lt;attribute name="source" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="version" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="accessionId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="locus" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *       &amp;lt;attribute name="canonical" type="{http://www.w3.org/2001/XMLSchema}boolean" default="false" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
@@ -186,6 +188,8 @@ public class Sequence
         protected String accessionId;
         @XmlAttribute(name = "locus")
         protected Boolean locus;
+        @XmlAttribute(name = "canonical")
+        protected Boolean canonical;
 
         /**
          * Gets the value of the mapping property.
@@ -311,6 +315,34 @@ public class Sequence
             this.locus = value;
         }
 
+        /**
+         * Gets the value of the canonical property.
+         * 
+         * @return
+         *     possible object is
+         *     {@link Boolean }
+         *     
+         */
+        public boolean isCanonical() {
+            if (canonical == null) {
+                return false;
+            } else {
+                return canonical;
+            }
+        }
+
+        /**
+         * Sets the value of the canonical property.
+         * 
+         * @param value
+         *     allowed object is
+         *     {@link Boolean }
+         *     
+         */
+        public void setCanonical(Boolean value) {
+            this.canonical = value;
+        }
+
     }
 
 }
index 6aee6ac..c69334e 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -19,35 +19,35 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for anonymous complex type.
+ * &lt;p&gt;Java class for anonymous complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;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 ref="{www.vamsas.ac.uk/jalview/version2}Sequence" maxOccurs="unbounded" minOccurs="0"/>
- *         &lt;element ref="{www.vamsas.ac.uk/jalview/version2}Annotation" maxOccurs="unbounded" minOccurs="0"/>
- *         &lt;element name="sequenceSetProperties" maxOccurs="unbounded" minOccurs="0">
- *           &lt;complexType>
- *             &lt;complexContent>
- *               &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 &lt;attribute name="key" type="{http://www.w3.org/2001/XMLSchema}string" />
- *                 &lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}string" />
- *               &lt;/restriction>
- *             &lt;/complexContent>
- *           &lt;/complexType>
- *         &lt;/element>
- *         &lt;element ref="{www.vamsas.ac.uk/jalview/version2}AlcodonFrame" maxOccurs="unbounded" minOccurs="0"/>
- *       &lt;/sequence>
- *       &lt;attribute name="gapChar" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="datasetId" type="{http://www.w3.org/2001/XMLSchema}string" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element ref="{www.vamsas.ac.uk/jalview/version2}Sequence" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element ref="{www.vamsas.ac.uk/jalview/version2}Annotation" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="sequenceSetProperties" maxOccurs="unbounded" minOccurs="0"&amp;gt;
+ *           &amp;lt;complexType&amp;gt;
+ *             &amp;lt;complexContent&amp;gt;
+ *               &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *                 &amp;lt;attribute name="key" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *                 &amp;lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *               &amp;lt;/restriction&amp;gt;
+ *             &amp;lt;/complexContent&amp;gt;
+ *           &amp;lt;/complexType&amp;gt;
+ *         &amp;lt;/element&amp;gt;
+ *         &amp;lt;element ref="{www.vamsas.ac.uk/jalview/version2}AlcodonFrame" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="gapChar" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="datasetId" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -76,20 +76,20 @@ public class SequenceSet {
     /**
      * Gets the value of the sequence property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 sequence property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the sequence property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getSequence().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link Sequence }
      * 
@@ -105,20 +105,20 @@ public class SequenceSet {
     /**
      * Gets the value of the annotation property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 annotation property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the annotation property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getAnnotation().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link Annotation }
      * 
@@ -134,20 +134,20 @@ public class SequenceSet {
     /**
      * Gets the value of the sequenceSetProperties property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 sequenceSetProperties property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the sequenceSetProperties property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getSequenceSetProperties().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link SequenceSet.SequenceSetProperties }
      * 
@@ -163,20 +163,20 @@ public class SequenceSet {
     /**
      * Gets the value of the alcodonFrame property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 alcodonFrame property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the alcodonFrame property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getAlcodonFrame().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link AlcodonFrame }
      * 
@@ -239,20 +239,20 @@ public class SequenceSet {
 
 
     /**
-     * <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="key" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *       &lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}string" />
-     *     &lt;/restriction>
-     *   &lt;/complexContent>
-     * &lt;/complexType>
-     * </pre>
+     * &lt;p&gt;Java class for anonymous complex type.
+     * 
+     * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
+     * 
+     * &lt;pre&gt;
+     * &amp;lt;complexType&amp;gt;
+     *   &amp;lt;complexContent&amp;gt;
+     *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+     *       &amp;lt;attribute name="key" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *       &amp;lt;attribute name="value" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+     *     &amp;lt;/restriction&amp;gt;
+     *   &amp;lt;/complexContent&amp;gt;
+     * &amp;lt;/complexType&amp;gt;
+     * &lt;/pre&gt;
      * 
      * 
      */
index aef7543..29982ae 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -16,24 +16,24 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for SequenceType complex type.
+ * &lt;p&gt;Java class for SequenceType complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="SequenceType">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;sequence>
- *         &lt;element name="sequence" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
- *         &lt;element name="name" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
- *       &lt;/sequence>
- *       &lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" />
- *       &lt;attribute name="description" type="{http://www.w3.org/2001/XMLSchema}string" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="SequenceType"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="sequence" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="name" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="id" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *       &amp;lt;attribute name="description" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
index 1b3d6d4..edb9152 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -13,19 +13,18 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for ThresholdType.
+ * &lt;p&gt;Java class for ThresholdType.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
- * <p>
- * <pre>
- * &lt;simpleType name="ThresholdType">
- *   &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string">
- *     &lt;enumeration value="NONE"/>
- *     &lt;enumeration value="ABOVE"/>
- *     &lt;enumeration value="BELOW"/>
- *   &lt;/restriction>
- * &lt;/simpleType>
- * </pre>
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
+ * &lt;pre&gt;
+ * &amp;lt;simpleType name="ThresholdType"&amp;gt;
+ *   &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}string"&amp;gt;
+ *     &amp;lt;enumeration value="NONE"/&amp;gt;
+ *     &amp;lt;enumeration value="ABOVE"/&amp;gt;
+ *     &amp;lt;enumeration value="BELOW"/&amp;gt;
+ *   &amp;lt;/restriction&amp;gt;
+ * &amp;lt;/simpleType&amp;gt;
+ * &lt;/pre&gt;
  * 
  */
 @XmlType(name = "ThresholdType", namespace = "www.jalview.org/colours")
index 5d341c3..48b2012 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -17,22 +17,22 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for VAMSAS complex type.
+ * &lt;p&gt;Java class for VAMSAS complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="VAMSAS">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;sequence>
- *         &lt;element name="Tree" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/>
- *         &lt;element ref="{www.vamsas.ac.uk/jalview/version2}SequenceSet" maxOccurs="unbounded" minOccurs="0"/>
- *       &lt;/sequence>
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="VAMSAS"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="Tree" type="{http://www.w3.org/2001/XMLSchema}string" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element ref="{www.vamsas.ac.uk/jalview/version2}SequenceSet" maxOccurs="unbounded" minOccurs="0"/&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -51,20 +51,20 @@ public class VAMSAS {
     /**
      * Gets the value of the tree property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the tree property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getTree().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link String }
      * 
@@ -80,20 +80,20 @@ public class VAMSAS {
     /**
      * Gets the value of the sequenceSet property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 sequenceSet property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the sequenceSet property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getSequenceSet().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link SequenceSet }
      * 
index 659eab9..87e275d 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 
@@ -20,25 +20,25 @@ import javax.xml.bind.annotation.XmlType;
 
 
 /**
- * <p>Java class for WebServiceParameterSet complex type.
+ * &lt;p&gt;Java class for WebServiceParameterSet complex type.
  * 
- * <p>The following schema fragment specifies the expected content contained within this class.
+ * &lt;p&gt;The following schema fragment specifies the expected content contained within this class.
  * 
- * <pre>
- * &lt;complexType name="WebServiceParameterSet">
- *   &lt;complexContent>
- *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       &lt;sequence>
- *         &lt;element name="Version" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
- *         &lt;element name="description" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
- *         &lt;element name="serviceURL" type="{http://www.w3.org/2001/XMLSchema}anyURI" maxOccurs="unbounded"/>
- *         &lt;element name="parameters" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *       &lt;/sequence>
- *       &lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
- *     &lt;/restriction>
- *   &lt;/complexContent>
- * &lt;/complexType>
- * </pre>
+ * &lt;pre&gt;
+ * &amp;lt;complexType name="WebServiceParameterSet"&amp;gt;
+ *   &amp;lt;complexContent&amp;gt;
+ *     &amp;lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&amp;gt;
+ *       &amp;lt;sequence&amp;gt;
+ *         &amp;lt;element name="Version" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="description" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&amp;gt;
+ *         &amp;lt;element name="serviceURL" type="{http://www.w3.org/2001/XMLSchema}anyURI" maxOccurs="unbounded"/&amp;gt;
+ *         &amp;lt;element name="parameters" type="{http://www.w3.org/2001/XMLSchema}string"/&amp;gt;
+ *       &amp;lt;/sequence&amp;gt;
+ *       &amp;lt;attribute name="name" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /&amp;gt;
+ *     &amp;lt;/restriction&amp;gt;
+ *   &amp;lt;/complexContent&amp;gt;
+ * &amp;lt;/complexType&amp;gt;
+ * &lt;/pre&gt;
  * 
  * 
  */
@@ -117,20 +117,20 @@ public class WebServiceParameterSet {
     /**
      * Gets the value of the serviceURL property.
      * 
-     * <p>
+     * &lt;p&gt;
      * 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 serviceURL property.
+     * This is why there is not a &lt;CODE&gt;set&lt;/CODE&gt; method for the serviceURL property.
      * 
-     * <p>
+     * &lt;p&gt;
      * For example, to add a new item, do as follows:
-     * <pre>
+     * &lt;pre&gt;
      *    getServiceURL().add(newItem);
-     * </pre>
+     * &lt;/pre&gt;
      * 
      * 
-     * <p>
+     * &lt;p&gt;
      * Objects of the following type(s) are allowed in the list
      * {@link String }
      * 
index 3ed532d..2700ffa 100644 (file)
@@ -1,8 +1,8 @@
 //
-// 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> 
+// This file was generated by the Eclipse Implementation of JAXB, v2.3.3 
+// See https://eclipse-ee4j.github.io/jaxb-ri 
 // Any modifications to this file will be lost upon recompilation of the source schema. 
-// Generated on: 2019.06.07 at 02:21:15 PM BST 
+// Generated on: 2021.08.30 at 11:05:22 AM BST 
 //
 
 @javax.xml.bind.annotation.XmlSchema(namespace = "www.vamsas.ac.uk/jalview/version2", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
index a3c8bee..e1c90f3 100755 (executable)
@@ -78,10 +78,27 @@ public class PDBChain
 
   public String pdbid = "";
 
-  public PDBChain(String thePdbid, String theId)
+  String tfacName = "Temperature Factor";
+
+
+  public PDBChain(String thePdbid, String theId, String tempFactorColumnName)
   {
     this.pdbid = thePdbid == null ? thePdbid : thePdbid.toLowerCase();
     this.id = theId;
+    if (tempFactorColumnName!=null && tempFactorColumnName.length()>0)
+    {
+      tfacName = tempFactorColumnName;
+    }
+  }
+
+  /**
+   * import chain data assuming Temperature Factor is in the Temperature Factor column
+   * @param thePdbid
+   * @param theId
+   */
+  public PDBChain(String thePdbid, String theId)
+  {
+    this(thePdbid,theId, null);
   }
 
   /**
@@ -490,15 +507,18 @@ public class PDBChain
         min = Math.min(min, annots[i].value);
         resAnnotation.setElementAt(null, i);
       }
-
       AlignmentAnnotation tfactorann = new AlignmentAnnotation(
-              "Temperature Factor", "Temperature Factor for " + pdbid + id,
+              tfacName, tfacName + " for " + pdbid + id,
               annots, min, max, AlignmentAnnotation.LINE_GRAPH);
+      
+      tfactorann.setCalcId(getClass().getName());
+
       tfactorann.setSequenceRef(sequence);
       sequence.addAlignmentAnnotation(tfactorann);
     }
   }
 
+
   /**
    * Colour start/end of bonds by charge
    * <ul>
@@ -615,8 +635,10 @@ public class PDBChain
 
         for (AlignmentAnnotation ana : shadow.getAnnotation())
         {
-          List<AlignmentAnnotation> transfer = sq
-                  .getAlignmentAnnotations(ana.getCalcId(), ana.label);
+          // match on calcId, label and description so annotations from
+          // different structures are preserved
+          List<AlignmentAnnotation> transfer = sq.getAlignmentAnnotations(
+                  ana.getCalcId(), ana.label, ana.description);
           if (transfer == null || transfer.size() == 0)
           {
             ana = new AlignmentAnnotation(ana);
@@ -636,8 +658,11 @@ public class PDBChain
         {
           for (AlignmentAnnotation ana : sequence.getAnnotation())
           {
+            // match on calcId, label and description so annotations from
+            // different structures are preserved
             List<AlignmentAnnotation> transfer = dsq
-                    .getAlignmentAnnotations(ana.getCalcId(), ana.label);
+                    .getAlignmentAnnotations(ana.getCalcId(), ana.label,
+                            ana.description);
             if (transfer == null || transfer.size() == 0)
             {
               ana = new AlignmentAnnotation(ana);
index 04eda42..0eb14cd 100755 (executable)
@@ -154,6 +154,7 @@ public class PDBfile extends StructureFile
             tmpchain.atoms.addElement(tmpatom);
           } else
           {
+            // PDBfile never handles alphafold models
             tmpchain = new PDBChain(getId(), tmpatom.chain);
             getChains().add(tmpchain);
             tmpchain.atoms.addElement(tmpatom);
diff --git a/swingjs/README.txt b/swingjs/README.txt
deleted file mode 100644 (file)
index f45850b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-The swingjs directory contains the current transpiler (net.sf.j2s.core.jar) 
-and the run-time core site files (SwingJS-site.zip)
-
-In addition are version directories -- for example, ver/3.1.1 and ver/3.2.1
-
-The second of these, ver/3.2.1, adds Java 8 functionality.
-
-
index 78d190b..f6fdabe 100644 (file)
Binary files a/swingjs/SwingJS-site.zip and b/swingjs/SwingJS-site.zip differ
diff --git a/swingjs/differences.txt b/swingjs/differences.txt
new file mode 100644 (file)
index 0000000..c9ec027
--- /dev/null
@@ -0,0 +1,1526 @@
+java2script/SwingJS Notes
+=========================
+
+updated 12/31/2020 -- full support for 64-bit long
+updated 12/6/2020 -- note about restrictions on long, including BitSet and Scanner
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+The goal of java2script/SwingJS is NOT to reproduce Java byte code processing in a 
+browser. We leave that task and its own issues to others. Here, instead, we have a 
+working JavaScript version of the Java classes along with runtime assistance in the
+j2sClazz.js library. This design has several advantages:
+
+ 1) It leads to much smaller downloads, since the class loader can dynamically load
+    code at the class level. 
+    
+ 2) It allow the browser to use its own optimizations and features, not to ignore those. 
+    This leads to huge performance gains and in many cases much simpler coding.
+ 3) It allows for in-browser debugging and analysis. 
+ 4) It allows for code switching between Java and JavaScript. Working Java code 
+    can be annotated (@j2sNative, @j2sAlias, @j2sIgnore) in a fashion that 
+    allows the code to run slightly differently in a Java than a JavaScript environment.
+    For example:
+    
+       int delayMS = /** @j2sNative 10 ||*/2;
+    
+    will read "var delayMS = 10 || 2;"  (i.e. 10) in JavaScript but read by the Java
+    compiler as "int delayMS = 2". 
+    
+ 5) Just generally, it allows for a much more integrated environment. JavaScript on
+    the page can call into any SwingJS program, and, likewise, any SwingJS code can 
+    access anything on the page.    
+
+
+Method and Field Disambiguation
+-------------------------------
+
+This is no problem. SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. 
+
+In Java, a class and its subclass can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified, and how individual methods
+can have more than one name using @j2sAlias. 
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem.   
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+The javajs.async package can be added to any Java program to provide Java+JavaScript asynchronous
+classes, including AsyncColorChooser, AsyncDialog, AsyncFileChooser, and AsyncSwingWorker. All
+of these classes work just as well in Java as in JavaScript. There is no need to run them 
+only when in JavaScript.
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity. Most access 
+to this package in working Java should be via the swingjs.api.JSUtilI interface.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java or JavaScript equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. Basically, what we have is a Java Swing "LookAndFeel" customized for HTML.
+
+The issue here is complex but workable. In Java there are two background concepts -- the 
+Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as BasicButtonUI or BasicTextFieldUI).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. They are chunks of low-level code that
+paint the screen to give the illusion that you really are pressing a button or typing text 
+ot the screen. Their events are being passed on to Java or the browser by the operating system. 
+
+UI classes provide a consistent "look and feel" for these native objects, rendering them onto 
+the native window canvas and handling all user-generated events. They paint the borders, 
+the backgrounds, the highlights, of every control you see in Java. There is one-to-one 
+correspondence of Swing classes and UI classes. Setting the Look and Feel for a project amounts 
+to selecting the directory from which to draw these UI classes. Java's UI class interfaces can
+be found in the javax.swing.plaf ("platform look and feel") package. Individual look and feel
+implementations are found in sun.plaf.basic, sun.plaf.metal, and other such specialized packages.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. However, this is not really an
+issue. We have reconfigured the class relationships a bit. In SwingJS, all AWT components
+are now subclasses of javax.swing.JComponent. While this might seem error prone, so far we have 
+found no problem with this arrangement. It's a little surprising to me that the original developers
+of Swing did not think of this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." 
+
+Then, to tie it all togeter, all AWT components such as java.awt.Button now subclass their respective
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. 
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+files read by SwingJS applications. That is because just peeking at a file 
+in SwingJS will load its entire byte[] data. 
+Optimally, all work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. The string name can be used alone, since SwingJS will
+cache the files itself and not reload them -- just as the browser normally does.
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine. Temporary files will be placed in the 
+"/TEMP/" directory, as seen from the running Java program. Any file written to this directory will
+simply be stored in memory; files written to any other directory, when closed, will appear to the 
+user as a download, often involving a "What do you want to do with this file" dialog. 
+
+
+See below for details relating to each of the subjects below:
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+none as of 2020.12.31. Source code for classes and methods missing
+from Java 8 or from Java 9+ can be inserted by any developer along 
+with their running code source, and they should run.  
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+primitive type restrictions - int, long, and float
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+no format internationalization
+Graphics2D: missing winding rules
+text-related field implementation
+Formatter/Regex limitations
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+primitive restrictions - int, long, and float
+---------------------------------------------
+
+int
+
+For performance reasons, int addition and multiplication do not by default overflow to 
+negative values. Instead, they just get bigger. Java code that relies on overflow to 
+negative values should be surrounded by ()|0 -- an OR with integer 0:
+
+
+int bigI, bigJ;
+...
+
+bigI = (bigI + bigJ)|0;
+
+bigI = (bigI + 1)|0;   //instead of bigI++
+
+
+Thus, in Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+This is because, generally, "-1" in JavaScript is not 0xFFFFFFFF. The simple ()|0 takes
+caes of this:
+
+               int newLength = (lineBuf.length * 2)|0;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+JavaScript does process bitwise operators & | ^ ~ properly for int values. There is no issue using
+these operations. 
+
+Note that int 1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+Importantly, the JavaScript Int32Array does behave properly. From the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. 
+
+long
+
+Java's 64-bit long type is fully supported, starting with java2script 3.3.1 (2020.12.31)
+The transpiler handles all conversions to and from long appropriately. See the discussion
+at https://github.com/BobHanson/java2script/issues/202 for how this is done.
+
+float
+
+SwingJS does not distinguish between float and double. Everything is double.
+
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. That is not a misprint. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript:
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true in SwingJS:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because, in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+java.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For a full discussion of modal dialogs, see the javajs.asyc.AsyncDialog.java discussion.
+
+For this action to work, the parent component must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this call.
+
+All of the standard Java events associated with Components are also available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ only the javadoc code will run in JavaScript, and only the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. 
+
+You can implement a simple name for a method using the @j2sAlias annoation in the javadoc for the 
+method involved. For example:
+
+
+/**
+ * @j2sAlias read
+ *
+ */
+public void read(byte[] buf, int pos, int len) {...}
+
+will allow the method to be accesible either as "read" or "read$BA$I$I" in JavaScript. 
+
+
+The default situation for this is a class name includes ".api.js" (case-sensitive). 
+This means that any method in any class in a package js within a package api, or any private interface js 
+that has an outer interface api, will have all-unqualified methods. An example of this is 
+swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics) call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+      
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+sun.awt.AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger and java.math.BigDecimal are fully supported. 
+
+
+no format internationalization
+------------------------------
+
+For now, just "en" for number and date formatters
+
+
+missing winding rules
+---------------------
+
+When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+In addition, JavaScript does not implement some of the more arcane POSIX {...} formats. 
+From java.util.regex.Pattern.java, we find the listing of conversions SwingJS does use:
+
+               "\\p{javaWhitespace}","\\s",
+               "\\p{javaDigit}","\\d",
+               "\\p{Lower}", "[a-z]",
+               "\\p{Upper}", "[A-Z]",
+               "\\p{ASCII}", "[\u0000-\u007F]",
+               "\\p{Alpha}", "[A-Za-z]",
+               "\\p{Digit}", "[0-9]",
+               "\\p{Alnum}", "[A-Za-z0-9]",
+               "\\p{Punct}", "[!\"#$%&'\\(\\)\\*\\+,-./:;<=>?@\\[\\\\\\]^_`{\\|}~]",
+               "\\p{Graph}", "[A-Za-z0-9]!\"#$%&'\\(\\)\\*\\+,-./:;<=>?@\\[\\\\\\]^_`{\\|}~]",
+               "\\p{Print}", "[A-Za-z0-9]!\"#$%&'\\(\\)\\*\\+,-./:;<=>?@\\[\\\\\\]^_`{\\|}~]",
+               "\\p{Blank}", "[ \t]",
+               "\\p{Cntrl}", "[\u0000-\u001F\u007F]",
+               "\\p{XDigit}", "[0-9a-fA-F]",
+               "\\p{Space}", "[ \t\n\u000B\f\r]",
+               "\\p{javaLowerCase}", "[a-z]",
+               "\\p{javaUpperCase}", "[A-Z]",
+               "\\p{Sc}", "[\u0024\u00A2\u00A3\u00A4\u00A5\u058F\u060B\u07FE\u07FF\u09F2\u09F3\u09FB\u0AF1\u0BF9\u0E3F\u20A0\u20A1\u20A2\u20A3\u20A4\u20A5\u20A6\u20A7\u20A8\u20A9\u20AA\u20AB\u20AC\u20AD\u20AE\u20AF\u20B0\u20B1\u20B2\u20B3\u20B4\u20B5\u20B6\u20B7\u20B8\u20B9\u20BA\u20BB\u20BC\u20BD\u20BE\u20BF\uA838\uFDFC\uFE69\uFF04\uFFE0\uFFE1\uFFE5\uFFE6]"
+
+Java's \Q \E quoting is handled appropriately.
+
+Additional Issues
+-----------------
+
+Method reflection is limited. Fields and methods do not retain public or default characteristics. 
+(This could be easily adapted, though.) Interfaces do not expose their methods, as the transpiler does not 
+actually transpile the interfaces themselves unless they contain default methods. 
+And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+
+
index 303fea5..53e50c6 100644 (file)
Binary files a/swingjs/net.sf.j2s.core-j11.jar and b/swingjs/net.sf.j2s.core-j11.jar differ
index 3638222..ced2ae7 100644 (file)
Binary files a/swingjs/net.sf.j2s.core.jar and b/swingjs/net.sf.j2s.core.jar differ
index 0d6b0a8..204bf8d 100644 (file)
@@ -1 +1 @@
-20200408084722 
+20210728172208 
diff --git a/swingjs/ver/3.2.10-j11/SwingJS-site.zip b/swingjs/ver/3.2.10-j11/SwingJS-site.zip
new file mode 100644 (file)
index 0000000..f12fcb1
Binary files /dev/null and b/swingjs/ver/3.2.10-j11/SwingJS-site.zip differ
similarity index 95%
rename from swingjs/ver/3.2.4/_j2sclasslist.txt
rename to swingjs/ver/3.2.10-j11/_j2sclasslist.txt
index e35111e..076f300 100644 (file)
@@ -72,6 +72,7 @@ java/awt/GraphicsCallback.js
 java/awt/GraphicsConfiguration.js
 java/awt/GraphicsDevice.js
 java/awt/GraphicsEnvironment.js
+java/awt/Image.js
 java/awt/image/ImageObserver.js
 java/awt/Insets.js
 java/awt/ItemSelectable.js
@@ -125,6 +126,9 @@ java/lang/ThreadGroup.js
 java/math/RoundingMode.js
 java/net/URL.js
 java/net/URLStreamHandlerFactory.js
+java/net/HttpURLConnection.js
+java/net/URLStreamHandler.js
+javax/net/ssl/HttpsUrlConnection.js
 java/text/CharacterIterator.js
 java/text/DecimalFormat.js
 java/text/DecimalFormatSymbols.js
@@ -173,8 +177,8 @@ javajs/util/AU.js
 javajs/util/JSThread.js
 javajs/util/Lst.js
 javajs/util/PT.js
+javajs/util/Rdr.js
 javajs/util/SB.js
-javax/net/ssl/HttpsUrlConnection.js
 javax/swing/AbstractAction.js
 javax/swing/AbstractButton.js
 javax/swing/AbstractListModel.js
@@ -189,6 +193,7 @@ javax/swing/border/CompoundBorder.js
 javax/swing/border/EmptyBorder.js
 javax/swing/border/EtchedBorder.js
 javax/swing/border/LineBorder.js
+javax/swing/border/TitledBorder.js
 javax/swing/BorderFactory.js
 javax/swing/BoundedRangeModel.js
 javax/swing/BoxLayout.js
@@ -221,6 +226,8 @@ javax/swing/LayoutFocusTraversalPolicy.js
 javax/swing/SortingFocusTraversalPolicy.js
 javax/swing/SwingContainerOrderFocusTraversalPolicy.js
 javax/swing/SwingDefaultFocusTraversalPolicy.js
+javax/swing/Icon.js
+javax/swing/ImageIcon.js
 javax/swing/InputMap.js
 javax/swing/JApplet.js
 javax/swing/JButton.js
@@ -317,12 +324,20 @@ sun/awt/RequestFocusController.js
 sun/awt/SunToolkit.js
 sun/awt/WindowClosingListener.js
 sun/awt/WindowClosingSupport.js
+sun/awt/image/DataStealer.js
+sun/awt/image/IntegerComponentRaster.js
+sun/awt/image/IntegerInterleavedRaster.js
+sun/awt/image/SunWritableRaster.js
 sun/font/FontDesignMetrics.js
 sun/swing/DefaultLookup.js
 sun/swing/SwingLazyValue.js
 sun/text/resources/FormatData.js
-sun/text/resources/FormatData_en.js
+sun/text/resources/en/FormatData_en.js
 sun/util/resources/LocaleData.js
+sun/util/locale/BaseLocale.js
+sun/util/locale/LocaleUtils.js
+sun/util/locale/provider/LocaleProviderAdapter.js
+sun/util/locale/provider/LocaleDataMetaInfo.js
 swingjs/a2s/A2SContainer.js
 swingjs/a2s/A2SEvent.js
 swingjs/a2s/A2SListener.js
@@ -348,6 +363,8 @@ swingjs/JSFrameViewer.js
 swingjs/JSGraphics2D.js
 swingjs/JSGraphicsConfiguration.js
 swingjs/JSGraphicsEnvironment.js
+swingjs/JSImage.js
+swingjs/JSImagekit.js
 swingjs/JSMouse.js
 swingjs/JSNullComponentPeer.js
 swingjs/JSScreenDevice.js
diff --git a/swingjs/ver/3.2.10-j11/differences.txt b/swingjs/ver/3.2.10-j11/differences.txt
new file mode 100644 (file)
index 0000000..60f5fcc
--- /dev/null
@@ -0,0 +1,1541 @@
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script 
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+  
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+updated 12/6/2020 -- note about restrictions on long, including BitSet and Scanner
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass 
+can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not 
+possible to cherry-pick methods to be unqualified; only full packages, classes or 
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem. 
+  
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. The issue here is complex but workable. In Java there are two background 
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to 
+Java or the browser by the operating system. UI classes provide a consistent "look and feel" 
+for these native objects, rendering them onto the native window canvas and handling all 
+user-generated events. They paint the borders, the backgrounds, the highlights, of every 
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes. 
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw 
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel") 
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. 
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of 
+javax.swing.JComponent. So far, we have found no problem with this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass 
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications. 
+All work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists. 
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+Restrictions on long
+Restriction on BitSet and Scanner
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented 
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the 
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+restrictions on long
+--------------------
+
+Java's 64-bit long type is not supported in JavaScript. There is no Int64Array in JavaScript,
+and 0x20000000000000 + 1 evaluates to 0x20000000000000, not 0x20000000000001. 
+(Likewise, -0x20000000000000 - 1 is left unchanged.) 
+
+The largest "integer" value in JavaScript is 9007199254740991 (9.007199254740991E13, or 0x1FFFFFFFFFFFFFF).
+Effectively, you get to use only 53 bits of the long, not 64. Trying to set a long larger than
+0x1FFFFFFFFFFFFFF or smaller than -0x1FFFFFFFFFFFFFF will result in a NumberFormatException.
+
+The transpiler handles conversion to long the same as Java for all cases other than from double. 
+
+For small double values, there is no problem, and, in fact, this is a known trick used to round 
+doubles and floats toward zero:
+
+double d;
+d = (long) 3.8;
+assert(d == 3);
+d = (long) -3.8;
+assert(d == -3);
+
+SwingJS will evaluate (long) d as 0 for d > 9007199254740991 
+or d < -9007199254740991, same as Java returns for Double.NaN.
+So, in Java we have:
+
+               assert(((long) Double.NaN) == 0);
+               assert(((int) Double.NaN) == 0);
+               assert(((long) Float.NaN) == 0);
+               assert(((int) Float.NaN) == 0);
+
+and also, in JavaScript only, we also have:
+
+               double d = 0x2000000000000L;
+               assert(((long) d) == 0);
+
+
+restrictions on BitSet and Scanner
+----------------------------------
+
+Because of the issue of long being only 53 bits, any time a method returns a long value, considerations must
+be made as to whether this will work in JavaScript. In particular, BitSet and Scanner have issues. 
+
+In SwingJS, java.util.BitSet has been implemented as a 32-bit integer-based bitset. This was no problem in
+Java 6, but starting with Java 7, a method was added to BitSet that allows for the extraction of the 
+underlying long[] word data. This is not work in JavaScript. Instead, SwingJS java.util.Bitset.toLongArray() will deliver 
+32-bit int[] data.
+
+SwingJS Scanner has hasNextLong() and nextLong(), and although it will scan through long numbers,
+Scanner will choke on long numbers greater than the JavaScript 53-bit limit. hasNextLong() will 
+return false, and nextLong() will throw an InputMismatchException triggered by the NumberFormatException
+thrown by Long.parseLong(). 
+
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in  JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080, 
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact, 
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From 
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. The default situation for this is a class name 
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within 
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development. 
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js, 
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics)
+call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+   
+   
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed 
+in and not fully tested (07/2019). 
+
+Both classes present significant issues for JavaScript, as they are based in 
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage 
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.  
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+  When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
diff --git a/swingjs/ver/3.2.10-j11/net.sf.j2s.core-j11.jar b/swingjs/ver/3.2.10-j11/net.sf.j2s.core-j11.jar
new file mode 100644 (file)
index 0000000..a999edf
Binary files /dev/null and b/swingjs/ver/3.2.10-j11/net.sf.j2s.core-j11.jar differ
diff --git a/swingjs/ver/3.2.10-j11/timestamp b/swingjs/ver/3.2.10-j11/timestamp
new file mode 100644 (file)
index 0000000..39964d7
--- /dev/null
@@ -0,0 +1 @@
+20201222130550 
diff --git a/swingjs/ver/3.2.10/DEV_NOTES.txt b/swingjs/ver/3.2.10/DEV_NOTES.txt
new file mode 100644 (file)
index 0000000..751d81c
--- /dev/null
@@ -0,0 +1,10 @@
+This is sources/net.sf.j2s.java.core/dist/DEV_NOTES.txt
+
+_j2sclasslist.txt 
+
+the list of .js files concatenated into coreswingjs.js and minified to coreswingjs.z.js
+
+
+SwingJS-site.zip
+
+the full site directory for SwingJS including all files not in the test/ directory.
diff --git a/swingjs/ver/3.2.10/SwingJS-site.zip b/swingjs/ver/3.2.10/SwingJS-site.zip
new file mode 100644 (file)
index 0000000..f12fcb1
Binary files /dev/null and b/swingjs/ver/3.2.10/SwingJS-site.zip differ
diff --git a/swingjs/ver/3.2.10/_j2sclasslist.txt b/swingjs/ver/3.2.10/_j2sclasslist.txt
new file mode 100644 (file)
index 0000000..076f300
--- /dev/null
@@ -0,0 +1,412 @@
+java/applet/Applet.js
+java/applet/AppletContext.js
+java/applet/AppletStub.js
+java/applet/JSApplet.js
+java/awt/ActiveEvent.js
+java/awt/Adjustable.js
+java/awt/AWTEvent.js
+java/awt/AWTEventMulticaster.js
+java/awt/AWTKeyStroke.js
+java/awt/BasicStroke.js
+java/awt/BorderLayout.js
+java/awt/Button.js
+java/awt/Color.js
+java/awt/color/ColorSpace.js
+java/awt/Component.js
+java/awt/ComponentOrientation.js
+java/awt/ContainerOrderFocusTraversalPolicy.js
+java/awt/Container.js
+java/awt/Cursor.js
+java/awt/DefaultFocusTraversalPolicy.js
+java/awt/DefaultKeyboardFocusManager.js
+java/awt/Dialog.js
+java/awt/Dimension.js
+java/awt/dnd/peer/DropTargetPeer.js
+java/awt/event/ActionListener.js
+java/awt/event/AdjustmentEvent.js
+java/awt/event/AdjustmentListener.js
+java/awt/event/AWTEventListener.js
+java/awt/event/ComponentAdapter.js
+java/awt/event/ComponentEvent.js
+java/awt/event/ComponentListener.js
+java/awt/event/ContainerListener.js
+java/awt/event/FocusEvent.js
+java/awt/event/FocusListener.js
+java/awt/event/HierarchyBoundsListener.js
+java/awt/event/HierarchyListener.js
+java/awt/event/InputEvent.js
+java/awt/event/InputMethodListener.js
+java/awt/event/InvocationEvent.js
+java/awt/event/ItemEvent.js
+java/awt/event/ItemListener.js
+java/awt/event/KeyListener.js
+java/awt/event/MouseEvent.js
+java/awt/event/MouseListener.js
+java/awt/event/MouseMotionListener.js
+java/awt/event/MouseWheelListener.js
+java/awt/event/TextListener.js
+java/awt/event/WindowAdapter.js
+java/awt/event/WindowEvent.js
+java/awt/event/WindowFocusListener.js
+java/awt/event/WindowListener.js
+java/awt/event/WindowStateListener.js
+java/awt/EventDispatchThread.js
+java/awt/EventFilter.js
+java/awt/EventQueue.js
+java/awt/EventQueueItem.js
+java/awt/FlowLayout.js
+java/awt/FocusTraversalPolicy.js
+java/awt/Font.js
+java/awt/font/FontRenderContext.js
+java/awt/FontMetrics.js
+java/awt/Frame.js
+java/awt/geom/AffineTransform.js
+java/awt/geom/Dimension2D.js
+java/awt/geom/Path2D.js
+java/awt/geom/PathIterator.js
+java/awt/geom/Point2D.js
+java/awt/geom/Rectangle2D.js
+java/awt/geom/RectangularShape.js
+java/awt/geom/RectIterator.js
+java/awt/GraphicsCallback.js
+java/awt/GraphicsConfiguration.js
+java/awt/GraphicsDevice.js
+java/awt/GraphicsEnvironment.js
+java/awt/Image.js
+java/awt/image/ImageObserver.js
+java/awt/Insets.js
+java/awt/ItemSelectable.js
+java/awt/JSComponent.js
+java/awt/JSDialog.js
+java/awt/JSFrame.js
+java/awt/JSPanel.js
+java/awt/KeyboardFocusManager.js
+java/awt/KeyEventDispatcher.js
+java/awt/KeyEventPostProcessor.js
+java/awt/Label.js
+java/awt/LayoutManager.js
+java/awt/LayoutManager2.js
+java/awt/LightweightDispatcher.js
+java/awt/Paint.js
+java/awt/Panel.js
+java/awt/peer/ComponentPeer.js
+java/awt/peer/ContainerPeer.js
+java/awt/peer/FramePeer.js
+java/awt/peer/KeyboardFocusManagerPeer.js
+java/awt/peer/LightweightPeer.js
+java/awt/peer/WindowPeer.js
+java/awt/Point.js
+java/awt/Queue.js
+java/awt/Rectangle.js
+java/awt/RenderingHints.js
+java/awt/Scrollbar.js
+java/awt/ScrollPane.js
+java/awt/Shape.js
+java/awt/Stroke.js
+java/awt/TextArea.js
+java/awt/TextComponent.js
+java/awt/TextField.js
+java/awt/Toolkit.js
+java/awt/Transparency.js
+java/awt/Window.js
+java/beans/ChangeListenerMap.js
+java/beans/PropertyChangeEvent.js
+java/beans/PropertyChangeListener.js
+java/beans/PropertyChangeSupport.js
+java/lang/AbstractStringBuilder.js
+java/lang/Class.js
+java/lang/Enum.js
+java/lang/Iterable.js
+java/lang/reflect/Constructor.js
+java/lang/reflect/Method.js
+java/lang/StringBuffer.js
+java/lang/StringBuilder.js
+java/lang/Thread.js
+java/lang/ThreadGroup.js
+java/math/RoundingMode.js
+java/net/URL.js
+java/net/URLStreamHandlerFactory.js
+java/net/HttpURLConnection.js
+java/net/URLStreamHandler.js
+javax/net/ssl/HttpsUrlConnection.js
+java/text/CharacterIterator.js
+java/text/DecimalFormat.js
+java/text/DecimalFormatSymbols.js
+java/text/DigitList.js
+java/text/FieldPosition.js
+java/text/Format.js
+java/text/NumberFormat.js
+java/util/AbstractCollection.js
+java/util/AbstractList.js
+java/util/AbstractMap.js
+java/util/AbstractSequentialList.js
+java/util/AbstractSet.js
+java/util/ArrayList.js
+java/util/Arrays.js
+java/util/Collection.js
+java/util/Collections.js
+java/util/Comparator.js
+java/util/Deque.js
+java/util/Dictionary.js
+java/util/Enumeration.js
+java/util/EventListener.js
+java/util/EventObject.js
+java/util/HashMap.js
+java/util/HashSet.js
+java/util/Hashtable.js
+java/util/IdentityHashMap.js
+java/util/Iterator.js
+java/util/LinkedHashMap.js
+java/util/LinkedList.js
+java/util/List.js
+java/util/ListResourceBundle.js
+java/util/Locale.js
+java/util/Map.js
+java/util/Objects.js
+java/util/Queue.js
+java/util/Random.js
+java/util/RandomAccess.js
+java/util/ResourceBundle.js
+java/util/Set.js
+java/util/TimSort.js
+java/util/Vector.js
+javajs/api/JSFunction.js
+javajs/util/AjaxURLConnection.js
+javajs/util/AjaxURLStreamHandlerFactory.js
+javajs/util/AU.js
+javajs/util/JSThread.js
+javajs/util/Lst.js
+javajs/util/PT.js
+javajs/util/Rdr.js
+javajs/util/SB.js
+javax/swing/AbstractAction.js
+javax/swing/AbstractButton.js
+javax/swing/AbstractListModel.js
+javax/swing/Action.js
+javax/swing/ActionMap.js
+javax/swing/AncestorNotifier.js
+javax/swing/ArrayTable.js
+javax/swing/border/AbstractBorder.js
+javax/swing/border/BevelBorder.js
+javax/swing/border/Border.js
+javax/swing/border/CompoundBorder.js
+javax/swing/border/EmptyBorder.js
+javax/swing/border/EtchedBorder.js
+javax/swing/border/LineBorder.js
+javax/swing/border/TitledBorder.js
+javax/swing/BorderFactory.js
+javax/swing/BoundedRangeModel.js
+javax/swing/BoxLayout.js
+javax/swing/ButtonGroup.js
+javax/swing/ButtonModel.js
+javax/swing/ClientPropertyKey.js
+javax/swing/ComboBoxModel.js
+javax/swing/DefaultBoundedRangeModel.js
+javax/swing/DefaultButtonModel.js
+javax/swing/DefaultComboBoxModel.js
+javax/swing/DefaultSingleSelectionModel.js
+javax/swing/DropMode.js
+javax/swing/event/AncestorEvent.js
+javax/swing/event/AncestorListener.js
+javax/swing/event/CaretEvent.js
+javax/swing/event/CaretListener.js
+javax/swing/event/ChangeEvent.js
+javax/swing/event/ChangeListener.js
+javax/swing/event/DocumentEvent.js
+javax/swing/event/DocumentListener.js
+javax/swing/event/EventListenerList.js
+javax/swing/event/ListDataEvent.js
+javax/swing/event/ListDataListener.js
+javax/swing/event/UndoableEditEvent.js
+javax/swing/event/UndoableEditListener.js
+javax/swing/FocusManager.js
+javax/swing/InternalFrameFocusTraversalPolicy.js
+javax/swing/LayoutComparator.js
+javax/swing/LayoutFocusTraversalPolicy.js
+javax/swing/SortingFocusTraversalPolicy.js
+javax/swing/SwingContainerOrderFocusTraversalPolicy.js
+javax/swing/SwingDefaultFocusTraversalPolicy.js
+javax/swing/Icon.js
+javax/swing/ImageIcon.js
+javax/swing/InputMap.js
+javax/swing/JApplet.js
+javax/swing/JButton.js
+javax/swing/JCheckBox.js
+javax/swing/JCheckBoxMenuItem.js
+javax/swing/JComboBox.js
+javax/swing/JComponent.js
+javax/swing/JFrame.js
+javax/swing/JLabel.js
+javax/swing/JLayeredPane.js
+javax/swing/JMenu.js
+javax/swing/JMenuBar.js
+javax/swing/JMenuItem.js
+javax/swing/JPanel.js
+javax/swing/JPopupMenu.js
+javax/swing/JRadioButtonMenuItem.js
+javax/swing/JRootPane.js
+javax/swing/JScrollBar.js
+javax/swing/JScrollPane.js
+javax/swing/JSeparator.js
+javax/swing/JTextArea.js
+javax/swing/JTextField.js
+javax/swing/JToggleButton.js
+javax/swing/JViewport.js
+javax/swing/KeyboardManager.js
+javax/swing/KeyStroke.js
+javax/swing/ListModel.js
+javax/swing/LookAndFeel.js
+javax/swing/MenuElement.js
+javax/swing/MutableComboBoxModel.js
+javax/swing/plaf/ActionMapUIResource.js
+javax/swing/plaf/basic/BasicBorders.js
+javax/swing/plaf/BorderUIResource.js
+javax/swing/plaf/ColorUIResource.js
+javax/swing/plaf/ComponentUI.js
+javax/swing/plaf/DimensionUIResource.js
+javax/swing/plaf/FontUIResource.js
+javax/swing/plaf/InputMapUIResource.js
+javax/swing/plaf/InsetsUIResource.js
+javax/swing/plaf/UIResource.js
+javax/swing/RepaintManager.js
+javax/swing/RootPaneContainer.js
+javax/swing/Scrollable.js
+javax/swing/ScrollPaneConstants.js
+javax/swing/ScrollPaneLayout.js
+javax/swing/SingleSelectionModel.js
+javax/swing/SizeRequirements.js
+javax/swing/SwingConstants.js
+javax/swing/SwingPaintEventDispatcher.js
+javax/swing/SwingUtilities.js
+javax/swing/text/AbstractDocument.js
+javax/swing/text/AttributeSet.js
+javax/swing/text/Caret.js
+javax/swing/text/DefaultCaret.js
+javax/swing/text/DefaultEditorKit.js
+javax/swing/text/Document.js
+javax/swing/text/EditorKit.js
+javax/swing/text/Element.js
+javax/swing/text/GapContent.js
+javax/swing/text/GapVector.js
+javax/swing/text/JTextComponent.js
+javax/swing/text/MutableAttributeSet.js
+javax/swing/text/PlainDocument.js
+javax/swing/text/PlainView.js
+javax/swing/text/Position.js
+javax/swing/text/Segment.js
+javax/swing/text/SegmentCache.js
+javax/swing/text/SimpleAttributeSet.js
+javax/swing/text/Style.js
+javax/swing/text/StyleConstants.js
+javax/swing/text/StyleContext.js
+javax/swing/text/TabExpander.js
+javax/swing/text/TextAction.js
+javax/swing/text/Utilities.js
+javax/swing/text/View.js
+javax/swing/tree/TreeNode.js
+javax/swing/UIDefaults.js
+javax/swing/UIManager.js
+javax/swing/undo/AbstractUndoableEdit.js
+javax/swing/undo/CompoundEdit.js
+javax/swing/undo/UndoableEdit.js
+javax/swing/ViewportLayout.js
+javax/swing/WindowConstants.js
+sun/awt/AppContext.js
+sun/awt/AWTAutoShutdown.js
+sun/awt/CausedFocusEvent.js
+sun/awt/ComponentFactory.js
+sun/awt/KeyboardFocusManagerPeerProvider.js
+sun/awt/MostRecentKeyValue.js
+sun/awt/MostRecentThreadAppContext.js
+sun/awt/PaintEventDispatcher.js
+sun/awt/PostEventQueue.js
+sun/awt/RequestFocusController.js
+sun/awt/SunToolkit.js
+sun/awt/WindowClosingListener.js
+sun/awt/WindowClosingSupport.js
+sun/awt/image/DataStealer.js
+sun/awt/image/IntegerComponentRaster.js
+sun/awt/image/IntegerInterleavedRaster.js
+sun/awt/image/SunWritableRaster.js
+sun/font/FontDesignMetrics.js
+sun/swing/DefaultLookup.js
+sun/swing/SwingLazyValue.js
+sun/text/resources/FormatData.js
+sun/text/resources/en/FormatData_en.js
+sun/util/resources/LocaleData.js
+sun/util/locale/BaseLocale.js
+sun/util/locale/LocaleUtils.js
+sun/util/locale/provider/LocaleProviderAdapter.js
+sun/util/locale/provider/LocaleDataMetaInfo.js
+swingjs/a2s/A2SContainer.js
+swingjs/a2s/A2SEvent.js
+swingjs/a2s/A2SListener.js
+swingjs/a2s/Applet.js
+swingjs/a2s/Button.js
+swingjs/a2s/Label.js
+swingjs/a2s/Panel.js
+swingjs/a2s/Scrollbar.js
+swingjs/a2s/ScrollPane.js
+swingjs/a2s/TextArea.js
+swingjs/a2s/TextField.js
+swingjs/api/Interface.js
+swingjs/api/js/DOMNode.js
+swingjs/api/js/HTML5CanvasContext2D.js
+swingjs/api/js/JSInterface.js
+swingjs/jquery/JQueryUI.js
+swingjs/JSApp.js
+swingjs/JSAppletThread.js
+swingjs/JSAppletViewer.js
+swingjs/JSFocusPeer.js
+swingjs/JSFontMetrics.js
+swingjs/JSFrameViewer.js
+swingjs/JSGraphics2D.js
+swingjs/JSGraphicsConfiguration.js
+swingjs/JSGraphicsEnvironment.js
+swingjs/JSImage.js
+swingjs/JSImagekit.js
+swingjs/JSMouse.js
+swingjs/JSNullComponentPeer.js
+swingjs/JSScreenDevice.js
+swingjs/JSThreadGroup.js
+swingjs/JSToolkit.js
+swingjs/JSUtil.js
+swingjs/plaf/ButtonListener.js
+swingjs/plaf/DefaultMenuLayout.js
+swingjs/plaf/HTML5LookAndFeel.js
+swingjs/plaf/JSAppletUI.js
+swingjs/plaf/JSButtonUI.js
+swingjs/plaf/JSCheckBoxMenuItemUI.js
+swingjs/plaf/JSCheckBoxUI.js
+swingjs/plaf/JSComboBoxUI.js
+swingjs/plaf/JSComponentUI.js
+swingjs/plaf/JSEventHandler.js
+swingjs/plaf/JSFrameUI.js
+swingjs/plaf/JSGraphicsUtils.js
+swingjs/plaf/JSLabelUI.js
+swingjs/plaf/JSLayeredPaneUI.js
+swingjs/plaf/JSLightweightUI.js
+swingjs/plaf/JSMenuBarUI.js
+swingjs/plaf/JSMenuItemUI.js
+swingjs/plaf/JSMenuUI.js
+swingjs/plaf/JSPanelUI.js
+swingjs/plaf/JSPopupMenuSeparatorUI.js
+swingjs/plaf/JSPopupMenuUI.js
+swingjs/plaf/JSRadioButtonMenuItemUI.js
+swingjs/plaf/JSRadioButtonUI.js
+swingjs/plaf/JSRootPaneUI.js
+swingjs/plaf/JSScrollBarUI.js
+swingjs/plaf/JSScrollPaneUI.js
+swingjs/plaf/JSSeparatorUI.js
+swingjs/plaf/JSSliderUI.js
+swingjs/plaf/JSTextAreaUI.js
+swingjs/plaf/JSTextFieldUI.js
+swingjs/plaf/JSTextUI.js
+swingjs/plaf/JSTextViewUI.js
+swingjs/plaf/JSViewportUI.js
+swingjs/plaf/JSWindowUI.js
+swingjs/plaf/LazyActionMap.js
+swingjs/plaf/Resizer.js
+swingjs/plaf/TextListener.js
+
+
diff --git a/swingjs/ver/3.2.10/differences.txt b/swingjs/ver/3.2.10/differences.txt
new file mode 100644 (file)
index 0000000..60f5fcc
--- /dev/null
@@ -0,0 +1,1541 @@
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script 
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+  
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+updated 12/6/2020 -- note about restrictions on long, including BitSet and Scanner
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass 
+can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not 
+possible to cherry-pick methods to be unqualified; only full packages, classes or 
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem. 
+  
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. The issue here is complex but workable. In Java there are two background 
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to 
+Java or the browser by the operating system. UI classes provide a consistent "look and feel" 
+for these native objects, rendering them onto the native window canvas and handling all 
+user-generated events. They paint the borders, the backgrounds, the highlights, of every 
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes. 
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw 
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel") 
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. 
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of 
+javax.swing.JComponent. So far, we have found no problem with this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass 
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications. 
+All work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists. 
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+Restrictions on long
+Restriction on BitSet and Scanner
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented 
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the 
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+restrictions on long
+--------------------
+
+Java's 64-bit long type is not supported in JavaScript. There is no Int64Array in JavaScript,
+and 0x20000000000000 + 1 evaluates to 0x20000000000000, not 0x20000000000001. 
+(Likewise, -0x20000000000000 - 1 is left unchanged.) 
+
+The largest "integer" value in JavaScript is 9007199254740991 (9.007199254740991E13, or 0x1FFFFFFFFFFFFFF).
+Effectively, you get to use only 53 bits of the long, not 64. Trying to set a long larger than
+0x1FFFFFFFFFFFFFF or smaller than -0x1FFFFFFFFFFFFFF will result in a NumberFormatException.
+
+The transpiler handles conversion to long the same as Java for all cases other than from double. 
+
+For small double values, there is no problem, and, in fact, this is a known trick used to round 
+doubles and floats toward zero:
+
+double d;
+d = (long) 3.8;
+assert(d == 3);
+d = (long) -3.8;
+assert(d == -3);
+
+SwingJS will evaluate (long) d as 0 for d > 9007199254740991 
+or d < -9007199254740991, same as Java returns for Double.NaN.
+So, in Java we have:
+
+               assert(((long) Double.NaN) == 0);
+               assert(((int) Double.NaN) == 0);
+               assert(((long) Float.NaN) == 0);
+               assert(((int) Float.NaN) == 0);
+
+and also, in JavaScript only, we also have:
+
+               double d = 0x2000000000000L;
+               assert(((long) d) == 0);
+
+
+restrictions on BitSet and Scanner
+----------------------------------
+
+Because of the issue of long being only 53 bits, any time a method returns a long value, considerations must
+be made as to whether this will work in JavaScript. In particular, BitSet and Scanner have issues. 
+
+In SwingJS, java.util.BitSet has been implemented as a 32-bit integer-based bitset. This was no problem in
+Java 6, but starting with Java 7, a method was added to BitSet that allows for the extraction of the 
+underlying long[] word data. This is not work in JavaScript. Instead, SwingJS java.util.Bitset.toLongArray() will deliver 
+32-bit int[] data.
+
+SwingJS Scanner has hasNextLong() and nextLong(), and although it will scan through long numbers,
+Scanner will choke on long numbers greater than the JavaScript 53-bit limit. hasNextLong() will 
+return false, and nextLong() will throw an InputMismatchException triggered by the NumberFormatException
+thrown by Long.parseLong(). 
+
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in  JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080, 
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact, 
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From 
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. The default situation for this is a class name 
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within 
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development. 
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js, 
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics)
+call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+   
+   
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed 
+in and not fully tested (07/2019). 
+
+Both classes present significant issues for JavaScript, as they are based in 
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage 
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.  
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+  When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
diff --git a/swingjs/ver/3.2.10/net.sf.j2s.core.jar b/swingjs/ver/3.2.10/net.sf.j2s.core.jar
new file mode 100644 (file)
index 0000000..30390e0
Binary files /dev/null and b/swingjs/ver/3.2.10/net.sf.j2s.core.jar differ
diff --git a/swingjs/ver/3.2.10/timestamp b/swingjs/ver/3.2.10/timestamp
new file mode 100644 (file)
index 0000000..bd4382b
--- /dev/null
@@ -0,0 +1 @@
+20201222130056 
diff --git a/swingjs/ver/3.2.4/SwingJS-site.zip b/swingjs/ver/3.2.4/SwingJS-site.zip
deleted file mode 100644 (file)
index 88ffc19..0000000
Binary files a/swingjs/ver/3.2.4/SwingJS-site.zip and /dev/null differ
diff --git a/swingjs/ver/3.2.4/net.sf.j2s.core.jar b/swingjs/ver/3.2.4/net.sf.j2s.core.jar
deleted file mode 100644 (file)
index e0cee1d..0000000
Binary files a/swingjs/ver/3.2.4/net.sf.j2s.core.jar and /dev/null differ
diff --git a/swingjs/ver/3.2.4/timestamp b/swingjs/ver/3.2.4/timestamp
deleted file mode 100644 (file)
index 5847eb0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-20191108205610 
index bec75f1..73853d4 100644 (file)
Binary files a/swingjs/ver/3.2.5/SwingJS-site.zip and b/swingjs/ver/3.2.5/SwingJS-site.zip differ
index e98f45d..076f300 100644 (file)
@@ -332,8 +332,12 @@ sun/font/FontDesignMetrics.js
 sun/swing/DefaultLookup.js
 sun/swing/SwingLazyValue.js
 sun/text/resources/FormatData.js
-sun/text/resources/FormatData_en.js
+sun/text/resources/en/FormatData_en.js
 sun/util/resources/LocaleData.js
+sun/util/locale/BaseLocale.js
+sun/util/locale/LocaleUtils.js
+sun/util/locale/provider/LocaleProviderAdapter.js
+sun/util/locale/provider/LocaleDataMetaInfo.js
 swingjs/a2s/A2SContainer.js
 swingjs/a2s/A2SEvent.js
 swingjs/a2s/A2SListener.js
diff --git a/swingjs/ver/3.2.5/differences.txt b/swingjs/ver/3.2.5/differences.txt
new file mode 100644 (file)
index 0000000..60f5fcc
--- /dev/null
@@ -0,0 +1,1541 @@
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script 
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+  
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+updated 12/6/2020 -- note about restrictions on long, including BitSet and Scanner
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass 
+can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not 
+possible to cherry-pick methods to be unqualified; only full packages, classes or 
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem. 
+  
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. The issue here is complex but workable. In Java there are two background 
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to 
+Java or the browser by the operating system. UI classes provide a consistent "look and feel" 
+for these native objects, rendering them onto the native window canvas and handling all 
+user-generated events. They paint the borders, the backgrounds, the highlights, of every 
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes. 
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw 
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel") 
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. 
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of 
+javax.swing.JComponent. So far, we have found no problem with this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass 
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications. 
+All work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists. 
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+Restrictions on long
+Restriction on BitSet and Scanner
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented 
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the 
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+restrictions on long
+--------------------
+
+Java's 64-bit long type is not supported in JavaScript. There is no Int64Array in JavaScript,
+and 0x20000000000000 + 1 evaluates to 0x20000000000000, not 0x20000000000001. 
+(Likewise, -0x20000000000000 - 1 is left unchanged.) 
+
+The largest "integer" value in JavaScript is 9007199254740991 (9.007199254740991E13, or 0x1FFFFFFFFFFFFFF).
+Effectively, you get to use only 53 bits of the long, not 64. Trying to set a long larger than
+0x1FFFFFFFFFFFFFF or smaller than -0x1FFFFFFFFFFFFFF will result in a NumberFormatException.
+
+The transpiler handles conversion to long the same as Java for all cases other than from double. 
+
+For small double values, there is no problem, and, in fact, this is a known trick used to round 
+doubles and floats toward zero:
+
+double d;
+d = (long) 3.8;
+assert(d == 3);
+d = (long) -3.8;
+assert(d == -3);
+
+SwingJS will evaluate (long) d as 0 for d > 9007199254740991 
+or d < -9007199254740991, same as Java returns for Double.NaN.
+So, in Java we have:
+
+               assert(((long) Double.NaN) == 0);
+               assert(((int) Double.NaN) == 0);
+               assert(((long) Float.NaN) == 0);
+               assert(((int) Float.NaN) == 0);
+
+and also, in JavaScript only, we also have:
+
+               double d = 0x2000000000000L;
+               assert(((long) d) == 0);
+
+
+restrictions on BitSet and Scanner
+----------------------------------
+
+Because of the issue of long being only 53 bits, any time a method returns a long value, considerations must
+be made as to whether this will work in JavaScript. In particular, BitSet and Scanner have issues. 
+
+In SwingJS, java.util.BitSet has been implemented as a 32-bit integer-based bitset. This was no problem in
+Java 6, but starting with Java 7, a method was added to BitSet that allows for the extraction of the 
+underlying long[] word data. This is not work in JavaScript. Instead, SwingJS java.util.Bitset.toLongArray() will deliver 
+32-bit int[] data.
+
+SwingJS Scanner has hasNextLong() and nextLong(), and although it will scan through long numbers,
+Scanner will choke on long numbers greater than the JavaScript 53-bit limit. hasNextLong() will 
+return false, and nextLong() will throw an InputMismatchException triggered by the NumberFormatException
+thrown by Long.parseLong(). 
+
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in  JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080, 
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact, 
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From 
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. The default situation for this is a class name 
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within 
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development. 
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js, 
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics)
+call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+   
+   
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed 
+in and not fully tested (07/2019). 
+
+Both classes present significant issues for JavaScript, as they are based in 
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage 
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.  
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+  When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
index e769633..057eacd 100644 (file)
Binary files a/swingjs/ver/3.2.5/net.sf.j2s.core.jar and b/swingjs/ver/3.2.5/net.sf.j2s.core.jar differ
index c1407cb..10cfe48 100644 (file)
@@ -1 +1 @@
-20191202082005 
+20201219175119 
index 763362e..dd6765a 100644 (file)
Binary files a/swingjs/ver/3.2.7/SwingJS-site.zip and b/swingjs/ver/3.2.7/SwingJS-site.zip differ
index 46f33d8..dd6765a 100644 (file)
Binary files a/swingjs/ver/3.2.8/SwingJS-site.zip and b/swingjs/ver/3.2.8/SwingJS-site.zip differ
index f644a9f..dd6765a 100644 (file)
Binary files a/swingjs/ver/3.2.9-j11/SwingJS-site.zip and b/swingjs/ver/3.2.9-j11/SwingJS-site.zip differ
diff --git a/swingjs/ver/3.2.9-j11/differences.txt b/swingjs/ver/3.2.9-j11/differences.txt
new file mode 100644 (file)
index 0000000..46e49ec
--- /dev/null
@@ -0,0 +1,1484 @@
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script 
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+  
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass 
+can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not 
+possible to cherry-pick methods to be unqualified; only full packages, classes or 
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem. 
+  
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. The issue here is complex but workable. In Java there are two background 
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to 
+Java or the browser by the operating system. UI classes provide a consistent "look and feel" 
+for these native objects, rendering them onto the native window canvas and handling all 
+user-generated events. They paint the borders, the backgrounds, the highlights, of every 
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes. 
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw 
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel") 
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. 
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of 
+javax.swing.JComponent. So far, we have found no problem with this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass 
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications. 
+All work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists. 
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented 
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the 
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in  JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080, 
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact, 
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From 
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. The default situation for this is a class name 
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within 
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development. 
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js, 
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics)
+call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+   
+   
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed 
+in and not fully tested (07/2019). 
+
+Both classes present significant issues for JavaScript, as they are based in 
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage 
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.  
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+  When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
diff --git a/swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar b/swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar
new file mode 100644 (file)
index 0000000..8cae046
Binary files /dev/null and b/swingjs/ver/3.2.9-j11/net.sf.j2s.core-j11.jar differ
diff --git a/swingjs/ver/3.2.9-j11/net.sf.j2s.core.jar b/swingjs/ver/3.2.9-j11/net.sf.j2s.core.jar
deleted file mode 100644 (file)
index 2a1593d..0000000
Binary files a/swingjs/ver/3.2.9-j11/net.sf.j2s.core.jar and /dev/null differ
index 78d190b..73853d4 100644 (file)
Binary files a/swingjs/ver/3.2.9/SwingJS-site.zip and b/swingjs/ver/3.2.9/SwingJS-site.zip differ
diff --git a/swingjs/ver/3.2.9/differences.txt b/swingjs/ver/3.2.9/differences.txt
new file mode 100644 (file)
index 0000000..60f5fcc
--- /dev/null
@@ -0,0 +1,1541 @@
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script 
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+  
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+updated 12/6/2020 -- note about restrictions on long, including BitSet and Scanner
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass 
+can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not 
+possible to cherry-pick methods to be unqualified; only full packages, classes or 
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem. 
+  
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. The issue here is complex but workable. In Java there are two background 
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to 
+Java or the browser by the operating system. UI classes provide a consistent "look and feel" 
+for these native objects, rendering them onto the native window canvas and handling all 
+user-generated events. They paint the borders, the backgrounds, the highlights, of every 
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes. 
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw 
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel") 
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. 
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of 
+javax.swing.JComponent. So far, we have found no problem with this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass 
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications. 
+All work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists. 
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+Restrictions on long
+Restriction on BitSet and Scanner
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented 
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the 
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+restrictions on long
+--------------------
+
+Java's 64-bit long type is not supported in JavaScript. There is no Int64Array in JavaScript,
+and 0x20000000000000 + 1 evaluates to 0x20000000000000, not 0x20000000000001. 
+(Likewise, -0x20000000000000 - 1 is left unchanged.) 
+
+The largest "integer" value in JavaScript is 9007199254740991 (9.007199254740991E13, or 0x1FFFFFFFFFFFFFF).
+Effectively, you get to use only 53 bits of the long, not 64. Trying to set a long larger than
+0x1FFFFFFFFFFFFFF or smaller than -0x1FFFFFFFFFFFFFF will result in a NumberFormatException.
+
+The transpiler handles conversion to long the same as Java for all cases other than from double. 
+
+For small double values, there is no problem, and, in fact, this is a known trick used to round 
+doubles and floats toward zero:
+
+double d;
+d = (long) 3.8;
+assert(d == 3);
+d = (long) -3.8;
+assert(d == -3);
+
+SwingJS will evaluate (long) d as 0 for d > 9007199254740991 
+or d < -9007199254740991, same as Java returns for Double.NaN.
+So, in Java we have:
+
+               assert(((long) Double.NaN) == 0);
+               assert(((int) Double.NaN) == 0);
+               assert(((long) Float.NaN) == 0);
+               assert(((int) Float.NaN) == 0);
+
+and also, in JavaScript only, we also have:
+
+               double d = 0x2000000000000L;
+               assert(((long) d) == 0);
+
+
+restrictions on BitSet and Scanner
+----------------------------------
+
+Because of the issue of long being only 53 bits, any time a method returns a long value, considerations must
+be made as to whether this will work in JavaScript. In particular, BitSet and Scanner have issues. 
+
+In SwingJS, java.util.BitSet has been implemented as a 32-bit integer-based bitset. This was no problem in
+Java 6, but starting with Java 7, a method was added to BitSet that allows for the extraction of the 
+underlying long[] word data. This is not work in JavaScript. Instead, SwingJS java.util.Bitset.toLongArray() will deliver 
+32-bit int[] data.
+
+SwingJS Scanner has hasNextLong() and nextLong(), and although it will scan through long numbers,
+Scanner will choke on long numbers greater than the JavaScript 53-bit limit. hasNextLong() will 
+return false, and nextLong() will throw an InputMismatchException triggered by the NumberFormatException
+thrown by Long.parseLong(). 
+
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in  JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080, 
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact, 
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From 
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. The default situation for this is a class name 
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within 
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development. 
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js, 
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics)
+call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+   
+   
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed 
+in and not fully tested (07/2019). 
+
+Both classes present significant issues for JavaScript, as they are based in 
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage 
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.  
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+  When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
diff --git a/swingjs/ver/3.2.9/net.sf.j2s.core-j11.jar b/swingjs/ver/3.2.9/net.sf.j2s.core-j11.jar
deleted file mode 100644 (file)
index 303fea5..0000000
Binary files a/swingjs/ver/3.2.9/net.sf.j2s.core-j11.jar and /dev/null differ
index 3638222..ce22a3e 100644 (file)
Binary files a/swingjs/ver/3.2.9/net.sf.j2s.core.jar and b/swingjs/ver/3.2.9/net.sf.j2s.core.jar differ
index 0d6b0a8..5232ef4 100644 (file)
@@ -1 +1 @@
-20200408084722 
+20201219150605 
diff --git a/swingjs/ver/3.3.1-j11/DEV_NOTES.txt b/swingjs/ver/3.3.1-j11/DEV_NOTES.txt
new file mode 100644 (file)
index 0000000..751d81c
--- /dev/null
@@ -0,0 +1,10 @@
+This is sources/net.sf.j2s.java.core/dist/DEV_NOTES.txt
+
+_j2sclasslist.txt 
+
+the list of .js files concatenated into coreswingjs.js and minified to coreswingjs.z.js
+
+
+SwingJS-site.zip
+
+the full site directory for SwingJS including all files not in the test/ directory.
diff --git a/swingjs/ver/3.3.1-j11/SwingJS-site.zip b/swingjs/ver/3.3.1-j11/SwingJS-site.zip
new file mode 100644 (file)
index 0000000..dafdec9
Binary files /dev/null and b/swingjs/ver/3.3.1-j11/SwingJS-site.zip differ
diff --git a/swingjs/ver/3.3.1-j11/_j2sclasslist.txt b/swingjs/ver/3.3.1-j11/_j2sclasslist.txt
new file mode 100644 (file)
index 0000000..076f300
--- /dev/null
@@ -0,0 +1,412 @@
+java/applet/Applet.js
+java/applet/AppletContext.js
+java/applet/AppletStub.js
+java/applet/JSApplet.js
+java/awt/ActiveEvent.js
+java/awt/Adjustable.js
+java/awt/AWTEvent.js
+java/awt/AWTEventMulticaster.js
+java/awt/AWTKeyStroke.js
+java/awt/BasicStroke.js
+java/awt/BorderLayout.js
+java/awt/Button.js
+java/awt/Color.js
+java/awt/color/ColorSpace.js
+java/awt/Component.js
+java/awt/ComponentOrientation.js
+java/awt/ContainerOrderFocusTraversalPolicy.js
+java/awt/Container.js
+java/awt/Cursor.js
+java/awt/DefaultFocusTraversalPolicy.js
+java/awt/DefaultKeyboardFocusManager.js
+java/awt/Dialog.js
+java/awt/Dimension.js
+java/awt/dnd/peer/DropTargetPeer.js
+java/awt/event/ActionListener.js
+java/awt/event/AdjustmentEvent.js
+java/awt/event/AdjustmentListener.js
+java/awt/event/AWTEventListener.js
+java/awt/event/ComponentAdapter.js
+java/awt/event/ComponentEvent.js
+java/awt/event/ComponentListener.js
+java/awt/event/ContainerListener.js
+java/awt/event/FocusEvent.js
+java/awt/event/FocusListener.js
+java/awt/event/HierarchyBoundsListener.js
+java/awt/event/HierarchyListener.js
+java/awt/event/InputEvent.js
+java/awt/event/InputMethodListener.js
+java/awt/event/InvocationEvent.js
+java/awt/event/ItemEvent.js
+java/awt/event/ItemListener.js
+java/awt/event/KeyListener.js
+java/awt/event/MouseEvent.js
+java/awt/event/MouseListener.js
+java/awt/event/MouseMotionListener.js
+java/awt/event/MouseWheelListener.js
+java/awt/event/TextListener.js
+java/awt/event/WindowAdapter.js
+java/awt/event/WindowEvent.js
+java/awt/event/WindowFocusListener.js
+java/awt/event/WindowListener.js
+java/awt/event/WindowStateListener.js
+java/awt/EventDispatchThread.js
+java/awt/EventFilter.js
+java/awt/EventQueue.js
+java/awt/EventQueueItem.js
+java/awt/FlowLayout.js
+java/awt/FocusTraversalPolicy.js
+java/awt/Font.js
+java/awt/font/FontRenderContext.js
+java/awt/FontMetrics.js
+java/awt/Frame.js
+java/awt/geom/AffineTransform.js
+java/awt/geom/Dimension2D.js
+java/awt/geom/Path2D.js
+java/awt/geom/PathIterator.js
+java/awt/geom/Point2D.js
+java/awt/geom/Rectangle2D.js
+java/awt/geom/RectangularShape.js
+java/awt/geom/RectIterator.js
+java/awt/GraphicsCallback.js
+java/awt/GraphicsConfiguration.js
+java/awt/GraphicsDevice.js
+java/awt/GraphicsEnvironment.js
+java/awt/Image.js
+java/awt/image/ImageObserver.js
+java/awt/Insets.js
+java/awt/ItemSelectable.js
+java/awt/JSComponent.js
+java/awt/JSDialog.js
+java/awt/JSFrame.js
+java/awt/JSPanel.js
+java/awt/KeyboardFocusManager.js
+java/awt/KeyEventDispatcher.js
+java/awt/KeyEventPostProcessor.js
+java/awt/Label.js
+java/awt/LayoutManager.js
+java/awt/LayoutManager2.js
+java/awt/LightweightDispatcher.js
+java/awt/Paint.js
+java/awt/Panel.js
+java/awt/peer/ComponentPeer.js
+java/awt/peer/ContainerPeer.js
+java/awt/peer/FramePeer.js
+java/awt/peer/KeyboardFocusManagerPeer.js
+java/awt/peer/LightweightPeer.js
+java/awt/peer/WindowPeer.js
+java/awt/Point.js
+java/awt/Queue.js
+java/awt/Rectangle.js
+java/awt/RenderingHints.js
+java/awt/Scrollbar.js
+java/awt/ScrollPane.js
+java/awt/Shape.js
+java/awt/Stroke.js
+java/awt/TextArea.js
+java/awt/TextComponent.js
+java/awt/TextField.js
+java/awt/Toolkit.js
+java/awt/Transparency.js
+java/awt/Window.js
+java/beans/ChangeListenerMap.js
+java/beans/PropertyChangeEvent.js
+java/beans/PropertyChangeListener.js
+java/beans/PropertyChangeSupport.js
+java/lang/AbstractStringBuilder.js
+java/lang/Class.js
+java/lang/Enum.js
+java/lang/Iterable.js
+java/lang/reflect/Constructor.js
+java/lang/reflect/Method.js
+java/lang/StringBuffer.js
+java/lang/StringBuilder.js
+java/lang/Thread.js
+java/lang/ThreadGroup.js
+java/math/RoundingMode.js
+java/net/URL.js
+java/net/URLStreamHandlerFactory.js
+java/net/HttpURLConnection.js
+java/net/URLStreamHandler.js
+javax/net/ssl/HttpsUrlConnection.js
+java/text/CharacterIterator.js
+java/text/DecimalFormat.js
+java/text/DecimalFormatSymbols.js
+java/text/DigitList.js
+java/text/FieldPosition.js
+java/text/Format.js
+java/text/NumberFormat.js
+java/util/AbstractCollection.js
+java/util/AbstractList.js
+java/util/AbstractMap.js
+java/util/AbstractSequentialList.js
+java/util/AbstractSet.js
+java/util/ArrayList.js
+java/util/Arrays.js
+java/util/Collection.js
+java/util/Collections.js
+java/util/Comparator.js
+java/util/Deque.js
+java/util/Dictionary.js
+java/util/Enumeration.js
+java/util/EventListener.js
+java/util/EventObject.js
+java/util/HashMap.js
+java/util/HashSet.js
+java/util/Hashtable.js
+java/util/IdentityHashMap.js
+java/util/Iterator.js
+java/util/LinkedHashMap.js
+java/util/LinkedList.js
+java/util/List.js
+java/util/ListResourceBundle.js
+java/util/Locale.js
+java/util/Map.js
+java/util/Objects.js
+java/util/Queue.js
+java/util/Random.js
+java/util/RandomAccess.js
+java/util/ResourceBundle.js
+java/util/Set.js
+java/util/TimSort.js
+java/util/Vector.js
+javajs/api/JSFunction.js
+javajs/util/AjaxURLConnection.js
+javajs/util/AjaxURLStreamHandlerFactory.js
+javajs/util/AU.js
+javajs/util/JSThread.js
+javajs/util/Lst.js
+javajs/util/PT.js
+javajs/util/Rdr.js
+javajs/util/SB.js
+javax/swing/AbstractAction.js
+javax/swing/AbstractButton.js
+javax/swing/AbstractListModel.js
+javax/swing/Action.js
+javax/swing/ActionMap.js
+javax/swing/AncestorNotifier.js
+javax/swing/ArrayTable.js
+javax/swing/border/AbstractBorder.js
+javax/swing/border/BevelBorder.js
+javax/swing/border/Border.js
+javax/swing/border/CompoundBorder.js
+javax/swing/border/EmptyBorder.js
+javax/swing/border/EtchedBorder.js
+javax/swing/border/LineBorder.js
+javax/swing/border/TitledBorder.js
+javax/swing/BorderFactory.js
+javax/swing/BoundedRangeModel.js
+javax/swing/BoxLayout.js
+javax/swing/ButtonGroup.js
+javax/swing/ButtonModel.js
+javax/swing/ClientPropertyKey.js
+javax/swing/ComboBoxModel.js
+javax/swing/DefaultBoundedRangeModel.js
+javax/swing/DefaultButtonModel.js
+javax/swing/DefaultComboBoxModel.js
+javax/swing/DefaultSingleSelectionModel.js
+javax/swing/DropMode.js
+javax/swing/event/AncestorEvent.js
+javax/swing/event/AncestorListener.js
+javax/swing/event/CaretEvent.js
+javax/swing/event/CaretListener.js
+javax/swing/event/ChangeEvent.js
+javax/swing/event/ChangeListener.js
+javax/swing/event/DocumentEvent.js
+javax/swing/event/DocumentListener.js
+javax/swing/event/EventListenerList.js
+javax/swing/event/ListDataEvent.js
+javax/swing/event/ListDataListener.js
+javax/swing/event/UndoableEditEvent.js
+javax/swing/event/UndoableEditListener.js
+javax/swing/FocusManager.js
+javax/swing/InternalFrameFocusTraversalPolicy.js
+javax/swing/LayoutComparator.js
+javax/swing/LayoutFocusTraversalPolicy.js
+javax/swing/SortingFocusTraversalPolicy.js
+javax/swing/SwingContainerOrderFocusTraversalPolicy.js
+javax/swing/SwingDefaultFocusTraversalPolicy.js
+javax/swing/Icon.js
+javax/swing/ImageIcon.js
+javax/swing/InputMap.js
+javax/swing/JApplet.js
+javax/swing/JButton.js
+javax/swing/JCheckBox.js
+javax/swing/JCheckBoxMenuItem.js
+javax/swing/JComboBox.js
+javax/swing/JComponent.js
+javax/swing/JFrame.js
+javax/swing/JLabel.js
+javax/swing/JLayeredPane.js
+javax/swing/JMenu.js
+javax/swing/JMenuBar.js
+javax/swing/JMenuItem.js
+javax/swing/JPanel.js
+javax/swing/JPopupMenu.js
+javax/swing/JRadioButtonMenuItem.js
+javax/swing/JRootPane.js
+javax/swing/JScrollBar.js
+javax/swing/JScrollPane.js
+javax/swing/JSeparator.js
+javax/swing/JTextArea.js
+javax/swing/JTextField.js
+javax/swing/JToggleButton.js
+javax/swing/JViewport.js
+javax/swing/KeyboardManager.js
+javax/swing/KeyStroke.js
+javax/swing/ListModel.js
+javax/swing/LookAndFeel.js
+javax/swing/MenuElement.js
+javax/swing/MutableComboBoxModel.js
+javax/swing/plaf/ActionMapUIResource.js
+javax/swing/plaf/basic/BasicBorders.js
+javax/swing/plaf/BorderUIResource.js
+javax/swing/plaf/ColorUIResource.js
+javax/swing/plaf/ComponentUI.js
+javax/swing/plaf/DimensionUIResource.js
+javax/swing/plaf/FontUIResource.js
+javax/swing/plaf/InputMapUIResource.js
+javax/swing/plaf/InsetsUIResource.js
+javax/swing/plaf/UIResource.js
+javax/swing/RepaintManager.js
+javax/swing/RootPaneContainer.js
+javax/swing/Scrollable.js
+javax/swing/ScrollPaneConstants.js
+javax/swing/ScrollPaneLayout.js
+javax/swing/SingleSelectionModel.js
+javax/swing/SizeRequirements.js
+javax/swing/SwingConstants.js
+javax/swing/SwingPaintEventDispatcher.js
+javax/swing/SwingUtilities.js
+javax/swing/text/AbstractDocument.js
+javax/swing/text/AttributeSet.js
+javax/swing/text/Caret.js
+javax/swing/text/DefaultCaret.js
+javax/swing/text/DefaultEditorKit.js
+javax/swing/text/Document.js
+javax/swing/text/EditorKit.js
+javax/swing/text/Element.js
+javax/swing/text/GapContent.js
+javax/swing/text/GapVector.js
+javax/swing/text/JTextComponent.js
+javax/swing/text/MutableAttributeSet.js
+javax/swing/text/PlainDocument.js
+javax/swing/text/PlainView.js
+javax/swing/text/Position.js
+javax/swing/text/Segment.js
+javax/swing/text/SegmentCache.js
+javax/swing/text/SimpleAttributeSet.js
+javax/swing/text/Style.js
+javax/swing/text/StyleConstants.js
+javax/swing/text/StyleContext.js
+javax/swing/text/TabExpander.js
+javax/swing/text/TextAction.js
+javax/swing/text/Utilities.js
+javax/swing/text/View.js
+javax/swing/tree/TreeNode.js
+javax/swing/UIDefaults.js
+javax/swing/UIManager.js
+javax/swing/undo/AbstractUndoableEdit.js
+javax/swing/undo/CompoundEdit.js
+javax/swing/undo/UndoableEdit.js
+javax/swing/ViewportLayout.js
+javax/swing/WindowConstants.js
+sun/awt/AppContext.js
+sun/awt/AWTAutoShutdown.js
+sun/awt/CausedFocusEvent.js
+sun/awt/ComponentFactory.js
+sun/awt/KeyboardFocusManagerPeerProvider.js
+sun/awt/MostRecentKeyValue.js
+sun/awt/MostRecentThreadAppContext.js
+sun/awt/PaintEventDispatcher.js
+sun/awt/PostEventQueue.js
+sun/awt/RequestFocusController.js
+sun/awt/SunToolkit.js
+sun/awt/WindowClosingListener.js
+sun/awt/WindowClosingSupport.js
+sun/awt/image/DataStealer.js
+sun/awt/image/IntegerComponentRaster.js
+sun/awt/image/IntegerInterleavedRaster.js
+sun/awt/image/SunWritableRaster.js
+sun/font/FontDesignMetrics.js
+sun/swing/DefaultLookup.js
+sun/swing/SwingLazyValue.js
+sun/text/resources/FormatData.js
+sun/text/resources/en/FormatData_en.js
+sun/util/resources/LocaleData.js
+sun/util/locale/BaseLocale.js
+sun/util/locale/LocaleUtils.js
+sun/util/locale/provider/LocaleProviderAdapter.js
+sun/util/locale/provider/LocaleDataMetaInfo.js
+swingjs/a2s/A2SContainer.js
+swingjs/a2s/A2SEvent.js
+swingjs/a2s/A2SListener.js
+swingjs/a2s/Applet.js
+swingjs/a2s/Button.js
+swingjs/a2s/Label.js
+swingjs/a2s/Panel.js
+swingjs/a2s/Scrollbar.js
+swingjs/a2s/ScrollPane.js
+swingjs/a2s/TextArea.js
+swingjs/a2s/TextField.js
+swingjs/api/Interface.js
+swingjs/api/js/DOMNode.js
+swingjs/api/js/HTML5CanvasContext2D.js
+swingjs/api/js/JSInterface.js
+swingjs/jquery/JQueryUI.js
+swingjs/JSApp.js
+swingjs/JSAppletThread.js
+swingjs/JSAppletViewer.js
+swingjs/JSFocusPeer.js
+swingjs/JSFontMetrics.js
+swingjs/JSFrameViewer.js
+swingjs/JSGraphics2D.js
+swingjs/JSGraphicsConfiguration.js
+swingjs/JSGraphicsEnvironment.js
+swingjs/JSImage.js
+swingjs/JSImagekit.js
+swingjs/JSMouse.js
+swingjs/JSNullComponentPeer.js
+swingjs/JSScreenDevice.js
+swingjs/JSThreadGroup.js
+swingjs/JSToolkit.js
+swingjs/JSUtil.js
+swingjs/plaf/ButtonListener.js
+swingjs/plaf/DefaultMenuLayout.js
+swingjs/plaf/HTML5LookAndFeel.js
+swingjs/plaf/JSAppletUI.js
+swingjs/plaf/JSButtonUI.js
+swingjs/plaf/JSCheckBoxMenuItemUI.js
+swingjs/plaf/JSCheckBoxUI.js
+swingjs/plaf/JSComboBoxUI.js
+swingjs/plaf/JSComponentUI.js
+swingjs/plaf/JSEventHandler.js
+swingjs/plaf/JSFrameUI.js
+swingjs/plaf/JSGraphicsUtils.js
+swingjs/plaf/JSLabelUI.js
+swingjs/plaf/JSLayeredPaneUI.js
+swingjs/plaf/JSLightweightUI.js
+swingjs/plaf/JSMenuBarUI.js
+swingjs/plaf/JSMenuItemUI.js
+swingjs/plaf/JSMenuUI.js
+swingjs/plaf/JSPanelUI.js
+swingjs/plaf/JSPopupMenuSeparatorUI.js
+swingjs/plaf/JSPopupMenuUI.js
+swingjs/plaf/JSRadioButtonMenuItemUI.js
+swingjs/plaf/JSRadioButtonUI.js
+swingjs/plaf/JSRootPaneUI.js
+swingjs/plaf/JSScrollBarUI.js
+swingjs/plaf/JSScrollPaneUI.js
+swingjs/plaf/JSSeparatorUI.js
+swingjs/plaf/JSSliderUI.js
+swingjs/plaf/JSTextAreaUI.js
+swingjs/plaf/JSTextFieldUI.js
+swingjs/plaf/JSTextUI.js
+swingjs/plaf/JSTextViewUI.js
+swingjs/plaf/JSViewportUI.js
+swingjs/plaf/JSWindowUI.js
+swingjs/plaf/LazyActionMap.js
+swingjs/plaf/Resizer.js
+swingjs/plaf/TextListener.js
+
+
diff --git a/swingjs/ver/3.3.1-j11/differences.txt b/swingjs/ver/3.3.1-j11/differences.txt
new file mode 100644 (file)
index 0000000..c9ec027
--- /dev/null
@@ -0,0 +1,1526 @@
+java2script/SwingJS Notes
+=========================
+
+updated 12/31/2020 -- full support for 64-bit long
+updated 12/6/2020 -- note about restrictions on long, including BitSet and Scanner
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+The goal of java2script/SwingJS is NOT to reproduce Java byte code processing in a 
+browser. We leave that task and its own issues to others. Here, instead, we have a 
+working JavaScript version of the Java classes along with runtime assistance in the
+j2sClazz.js library. This design has several advantages:
+
+ 1) It leads to much smaller downloads, since the class loader can dynamically load
+    code at the class level. 
+    
+ 2) It allow the browser to use its own optimizations and features, not to ignore those. 
+    This leads to huge performance gains and in many cases much simpler coding.
+ 3) It allows for in-browser debugging and analysis. 
+ 4) It allows for code switching between Java and JavaScript. Working Java code 
+    can be annotated (@j2sNative, @j2sAlias, @j2sIgnore) in a fashion that 
+    allows the code to run slightly differently in a Java than a JavaScript environment.
+    For example:
+    
+       int delayMS = /** @j2sNative 10 ||*/2;
+    
+    will read "var delayMS = 10 || 2;"  (i.e. 10) in JavaScript but read by the Java
+    compiler as "int delayMS = 2". 
+    
+ 5) Just generally, it allows for a much more integrated environment. JavaScript on
+    the page can call into any SwingJS program, and, likewise, any SwingJS code can 
+    access anything on the page.    
+
+
+Method and Field Disambiguation
+-------------------------------
+
+This is no problem. SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. 
+
+In Java, a class and its subclass can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified, and how individual methods
+can have more than one name using @j2sAlias. 
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem.   
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+The javajs.async package can be added to any Java program to provide Java+JavaScript asynchronous
+classes, including AsyncColorChooser, AsyncDialog, AsyncFileChooser, and AsyncSwingWorker. All
+of these classes work just as well in Java as in JavaScript. There is no need to run them 
+only when in JavaScript.
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity. Most access 
+to this package in working Java should be via the swingjs.api.JSUtilI interface.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java or JavaScript equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. Basically, what we have is a Java Swing "LookAndFeel" customized for HTML.
+
+The issue here is complex but workable. In Java there are two background concepts -- the 
+Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as BasicButtonUI or BasicTextFieldUI).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. They are chunks of low-level code that
+paint the screen to give the illusion that you really are pressing a button or typing text 
+ot the screen. Their events are being passed on to Java or the browser by the operating system. 
+
+UI classes provide a consistent "look and feel" for these native objects, rendering them onto 
+the native window canvas and handling all user-generated events. They paint the borders, 
+the backgrounds, the highlights, of every control you see in Java. There is one-to-one 
+correspondence of Swing classes and UI classes. Setting the Look and Feel for a project amounts 
+to selecting the directory from which to draw these UI classes. Java's UI class interfaces can
+be found in the javax.swing.plaf ("platform look and feel") package. Individual look and feel
+implementations are found in sun.plaf.basic, sun.plaf.metal, and other such specialized packages.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. However, this is not really an
+issue. We have reconfigured the class relationships a bit. In SwingJS, all AWT components
+are now subclasses of javax.swing.JComponent. While this might seem error prone, so far we have 
+found no problem with this arrangement. It's a little surprising to me that the original developers
+of Swing did not think of this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." 
+
+Then, to tie it all togeter, all AWT components such as java.awt.Button now subclass their respective
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. 
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+files read by SwingJS applications. That is because just peeking at a file 
+in SwingJS will load its entire byte[] data. 
+Optimally, all work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. The string name can be used alone, since SwingJS will
+cache the files itself and not reload them -- just as the browser normally does.
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine. Temporary files will be placed in the 
+"/TEMP/" directory, as seen from the running Java program. Any file written to this directory will
+simply be stored in memory; files written to any other directory, when closed, will appear to the 
+user as a download, often involving a "What do you want to do with this file" dialog. 
+
+
+See below for details relating to each of the subjects below:
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+none as of 2020.12.31. Source code for classes and methods missing
+from Java 8 or from Java 9+ can be inserted by any developer along 
+with their running code source, and they should run.  
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+primitive type restrictions - int, long, and float
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+no format internationalization
+Graphics2D: missing winding rules
+text-related field implementation
+Formatter/Regex limitations
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+primitive restrictions - int, long, and float
+---------------------------------------------
+
+int
+
+For performance reasons, int addition and multiplication do not by default overflow to 
+negative values. Instead, they just get bigger. Java code that relies on overflow to 
+negative values should be surrounded by ()|0 -- an OR with integer 0:
+
+
+int bigI, bigJ;
+...
+
+bigI = (bigI + bigJ)|0;
+
+bigI = (bigI + 1)|0;   //instead of bigI++
+
+
+Thus, in Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+This is because, generally, "-1" in JavaScript is not 0xFFFFFFFF. The simple ()|0 takes
+caes of this:
+
+               int newLength = (lineBuf.length * 2)|0;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+JavaScript does process bitwise operators & | ^ ~ properly for int values. There is no issue using
+these operations. 
+
+Note that int 1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+Importantly, the JavaScript Int32Array does behave properly. From the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. 
+
+long
+
+Java's 64-bit long type is fully supported, starting with java2script 3.3.1 (2020.12.31)
+The transpiler handles all conversions to and from long appropriately. See the discussion
+at https://github.com/BobHanson/java2script/issues/202 for how this is done.
+
+float
+
+SwingJS does not distinguish between float and double. Everything is double.
+
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. That is not a misprint. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript:
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true in SwingJS:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because, in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+java.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For a full discussion of modal dialogs, see the javajs.asyc.AsyncDialog.java discussion.
+
+For this action to work, the parent component must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this call.
+
+All of the standard Java events associated with Components are also available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ only the javadoc code will run in JavaScript, and only the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. 
+
+You can implement a simple name for a method using the @j2sAlias annoation in the javadoc for the 
+method involved. For example:
+
+
+/**
+ * @j2sAlias read
+ *
+ */
+public void read(byte[] buf, int pos, int len) {...}
+
+will allow the method to be accesible either as "read" or "read$BA$I$I" in JavaScript. 
+
+
+The default situation for this is a class name includes ".api.js" (case-sensitive). 
+This means that any method in any class in a package js within a package api, or any private interface js 
+that has an outer interface api, will have all-unqualified methods. An example of this is 
+swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics) call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+      
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+sun.awt.AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger and java.math.BigDecimal are fully supported. 
+
+
+no format internationalization
+------------------------------
+
+For now, just "en" for number and date formatters
+
+
+missing winding rules
+---------------------
+
+When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+In addition, JavaScript does not implement some of the more arcane POSIX {...} formats. 
+From java.util.regex.Pattern.java, we find the listing of conversions SwingJS does use:
+
+               "\\p{javaWhitespace}","\\s",
+               "\\p{javaDigit}","\\d",
+               "\\p{Lower}", "[a-z]",
+               "\\p{Upper}", "[A-Z]",
+               "\\p{ASCII}", "[\u0000-\u007F]",
+               "\\p{Alpha}", "[A-Za-z]",
+               "\\p{Digit}", "[0-9]",
+               "\\p{Alnum}", "[A-Za-z0-9]",
+               "\\p{Punct}", "[!\"#$%&'\\(\\)\\*\\+,-./:;<=>?@\\[\\\\\\]^_`{\\|}~]",
+               "\\p{Graph}", "[A-Za-z0-9]!\"#$%&'\\(\\)\\*\\+,-./:;<=>?@\\[\\\\\\]^_`{\\|}~]",
+               "\\p{Print}", "[A-Za-z0-9]!\"#$%&'\\(\\)\\*\\+,-./:;<=>?@\\[\\\\\\]^_`{\\|}~]",
+               "\\p{Blank}", "[ \t]",
+               "\\p{Cntrl}", "[\u0000-\u001F\u007F]",
+               "\\p{XDigit}", "[0-9a-fA-F]",
+               "\\p{Space}", "[ \t\n\u000B\f\r]",
+               "\\p{javaLowerCase}", "[a-z]",
+               "\\p{javaUpperCase}", "[A-Z]",
+               "\\p{Sc}", "[\u0024\u00A2\u00A3\u00A4\u00A5\u058F\u060B\u07FE\u07FF\u09F2\u09F3\u09FB\u0AF1\u0BF9\u0E3F\u20A0\u20A1\u20A2\u20A3\u20A4\u20A5\u20A6\u20A7\u20A8\u20A9\u20AA\u20AB\u20AC\u20AD\u20AE\u20AF\u20B0\u20B1\u20B2\u20B3\u20B4\u20B5\u20B6\u20B7\u20B8\u20B9\u20BA\u20BB\u20BC\u20BD\u20BE\u20BF\uA838\uFDFC\uFE69\uFF04\uFFE0\uFFE1\uFFE5\uFFE6]"
+
+Java's \Q \E quoting is handled appropriately.
+
+Additional Issues
+-----------------
+
+Method reflection is limited. Fields and methods do not retain public or default characteristics. 
+(This could be easily adapted, though.) Interfaces do not expose their methods, as the transpiler does not 
+actually transpile the interfaces themselves unless they contain default methods. 
+And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+
+
diff --git a/swingjs/ver/3.3.1-j11/net.sf.j2s.core-j11.jar b/swingjs/ver/3.3.1-j11/net.sf.j2s.core-j11.jar
new file mode 100644 (file)
index 0000000..53e50c6
Binary files /dev/null and b/swingjs/ver/3.3.1-j11/net.sf.j2s.core-j11.jar differ
diff --git a/swingjs/ver/3.3.1-j11/timestamp b/swingjs/ver/3.3.1-j11/timestamp
new file mode 100644 (file)
index 0000000..9b5ac4e
--- /dev/null
@@ -0,0 +1 @@
+20210208070817 
diff --git a/swingjs/ver/3.3.1/DEV_NOTES.txt b/swingjs/ver/3.3.1/DEV_NOTES.txt
new file mode 100644 (file)
index 0000000..751d81c
--- /dev/null
@@ -0,0 +1,10 @@
+This is sources/net.sf.j2s.java.core/dist/DEV_NOTES.txt
+
+_j2sclasslist.txt 
+
+the list of .js files concatenated into coreswingjs.js and minified to coreswingjs.z.js
+
+
+SwingJS-site.zip
+
+the full site directory for SwingJS including all files not in the test/ directory.
diff --git a/swingjs/ver/3.3.1/SwingJS-site.zip b/swingjs/ver/3.3.1/SwingJS-site.zip
new file mode 100644 (file)
index 0000000..f6fdabe
Binary files /dev/null and b/swingjs/ver/3.3.1/SwingJS-site.zip differ
diff --git a/swingjs/ver/3.3.1/_j2sclasslist.txt b/swingjs/ver/3.3.1/_j2sclasslist.txt
new file mode 100644 (file)
index 0000000..076f300
--- /dev/null
@@ -0,0 +1,412 @@
+java/applet/Applet.js
+java/applet/AppletContext.js
+java/applet/AppletStub.js
+java/applet/JSApplet.js
+java/awt/ActiveEvent.js
+java/awt/Adjustable.js
+java/awt/AWTEvent.js
+java/awt/AWTEventMulticaster.js
+java/awt/AWTKeyStroke.js
+java/awt/BasicStroke.js
+java/awt/BorderLayout.js
+java/awt/Button.js
+java/awt/Color.js
+java/awt/color/ColorSpace.js
+java/awt/Component.js
+java/awt/ComponentOrientation.js
+java/awt/ContainerOrderFocusTraversalPolicy.js
+java/awt/Container.js
+java/awt/Cursor.js
+java/awt/DefaultFocusTraversalPolicy.js
+java/awt/DefaultKeyboardFocusManager.js
+java/awt/Dialog.js
+java/awt/Dimension.js
+java/awt/dnd/peer/DropTargetPeer.js
+java/awt/event/ActionListener.js
+java/awt/event/AdjustmentEvent.js
+java/awt/event/AdjustmentListener.js
+java/awt/event/AWTEventListener.js
+java/awt/event/ComponentAdapter.js
+java/awt/event/ComponentEvent.js
+java/awt/event/ComponentListener.js
+java/awt/event/ContainerListener.js
+java/awt/event/FocusEvent.js
+java/awt/event/FocusListener.js
+java/awt/event/HierarchyBoundsListener.js
+java/awt/event/HierarchyListener.js
+java/awt/event/InputEvent.js
+java/awt/event/InputMethodListener.js
+java/awt/event/InvocationEvent.js
+java/awt/event/ItemEvent.js
+java/awt/event/ItemListener.js
+java/awt/event/KeyListener.js
+java/awt/event/MouseEvent.js
+java/awt/event/MouseListener.js
+java/awt/event/MouseMotionListener.js
+java/awt/event/MouseWheelListener.js
+java/awt/event/TextListener.js
+java/awt/event/WindowAdapter.js
+java/awt/event/WindowEvent.js
+java/awt/event/WindowFocusListener.js
+java/awt/event/WindowListener.js
+java/awt/event/WindowStateListener.js
+java/awt/EventDispatchThread.js
+java/awt/EventFilter.js
+java/awt/EventQueue.js
+java/awt/EventQueueItem.js
+java/awt/FlowLayout.js
+java/awt/FocusTraversalPolicy.js
+java/awt/Font.js
+java/awt/font/FontRenderContext.js
+java/awt/FontMetrics.js
+java/awt/Frame.js
+java/awt/geom/AffineTransform.js
+java/awt/geom/Dimension2D.js
+java/awt/geom/Path2D.js
+java/awt/geom/PathIterator.js
+java/awt/geom/Point2D.js
+java/awt/geom/Rectangle2D.js
+java/awt/geom/RectangularShape.js
+java/awt/geom/RectIterator.js
+java/awt/GraphicsCallback.js
+java/awt/GraphicsConfiguration.js
+java/awt/GraphicsDevice.js
+java/awt/GraphicsEnvironment.js
+java/awt/Image.js
+java/awt/image/ImageObserver.js
+java/awt/Insets.js
+java/awt/ItemSelectable.js
+java/awt/JSComponent.js
+java/awt/JSDialog.js
+java/awt/JSFrame.js
+java/awt/JSPanel.js
+java/awt/KeyboardFocusManager.js
+java/awt/KeyEventDispatcher.js
+java/awt/KeyEventPostProcessor.js
+java/awt/Label.js
+java/awt/LayoutManager.js
+java/awt/LayoutManager2.js
+java/awt/LightweightDispatcher.js
+java/awt/Paint.js
+java/awt/Panel.js
+java/awt/peer/ComponentPeer.js
+java/awt/peer/ContainerPeer.js
+java/awt/peer/FramePeer.js
+java/awt/peer/KeyboardFocusManagerPeer.js
+java/awt/peer/LightweightPeer.js
+java/awt/peer/WindowPeer.js
+java/awt/Point.js
+java/awt/Queue.js
+java/awt/Rectangle.js
+java/awt/RenderingHints.js
+java/awt/Scrollbar.js
+java/awt/ScrollPane.js
+java/awt/Shape.js
+java/awt/Stroke.js
+java/awt/TextArea.js
+java/awt/TextComponent.js
+java/awt/TextField.js
+java/awt/Toolkit.js
+java/awt/Transparency.js
+java/awt/Window.js
+java/beans/ChangeListenerMap.js
+java/beans/PropertyChangeEvent.js
+java/beans/PropertyChangeListener.js
+java/beans/PropertyChangeSupport.js
+java/lang/AbstractStringBuilder.js
+java/lang/Class.js
+java/lang/Enum.js
+java/lang/Iterable.js
+java/lang/reflect/Constructor.js
+java/lang/reflect/Method.js
+java/lang/StringBuffer.js
+java/lang/StringBuilder.js
+java/lang/Thread.js
+java/lang/ThreadGroup.js
+java/math/RoundingMode.js
+java/net/URL.js
+java/net/URLStreamHandlerFactory.js
+java/net/HttpURLConnection.js
+java/net/URLStreamHandler.js
+javax/net/ssl/HttpsUrlConnection.js
+java/text/CharacterIterator.js
+java/text/DecimalFormat.js
+java/text/DecimalFormatSymbols.js
+java/text/DigitList.js
+java/text/FieldPosition.js
+java/text/Format.js
+java/text/NumberFormat.js
+java/util/AbstractCollection.js
+java/util/AbstractList.js
+java/util/AbstractMap.js
+java/util/AbstractSequentialList.js
+java/util/AbstractSet.js
+java/util/ArrayList.js
+java/util/Arrays.js
+java/util/Collection.js
+java/util/Collections.js
+java/util/Comparator.js
+java/util/Deque.js
+java/util/Dictionary.js
+java/util/Enumeration.js
+java/util/EventListener.js
+java/util/EventObject.js
+java/util/HashMap.js
+java/util/HashSet.js
+java/util/Hashtable.js
+java/util/IdentityHashMap.js
+java/util/Iterator.js
+java/util/LinkedHashMap.js
+java/util/LinkedList.js
+java/util/List.js
+java/util/ListResourceBundle.js
+java/util/Locale.js
+java/util/Map.js
+java/util/Objects.js
+java/util/Queue.js
+java/util/Random.js
+java/util/RandomAccess.js
+java/util/ResourceBundle.js
+java/util/Set.js
+java/util/TimSort.js
+java/util/Vector.js
+javajs/api/JSFunction.js
+javajs/util/AjaxURLConnection.js
+javajs/util/AjaxURLStreamHandlerFactory.js
+javajs/util/AU.js
+javajs/util/JSThread.js
+javajs/util/Lst.js
+javajs/util/PT.js
+javajs/util/Rdr.js
+javajs/util/SB.js
+javax/swing/AbstractAction.js
+javax/swing/AbstractButton.js
+javax/swing/AbstractListModel.js
+javax/swing/Action.js
+javax/swing/ActionMap.js
+javax/swing/AncestorNotifier.js
+javax/swing/ArrayTable.js
+javax/swing/border/AbstractBorder.js
+javax/swing/border/BevelBorder.js
+javax/swing/border/Border.js
+javax/swing/border/CompoundBorder.js
+javax/swing/border/EmptyBorder.js
+javax/swing/border/EtchedBorder.js
+javax/swing/border/LineBorder.js
+javax/swing/border/TitledBorder.js
+javax/swing/BorderFactory.js
+javax/swing/BoundedRangeModel.js
+javax/swing/BoxLayout.js
+javax/swing/ButtonGroup.js
+javax/swing/ButtonModel.js
+javax/swing/ClientPropertyKey.js
+javax/swing/ComboBoxModel.js
+javax/swing/DefaultBoundedRangeModel.js
+javax/swing/DefaultButtonModel.js
+javax/swing/DefaultComboBoxModel.js
+javax/swing/DefaultSingleSelectionModel.js
+javax/swing/DropMode.js
+javax/swing/event/AncestorEvent.js
+javax/swing/event/AncestorListener.js
+javax/swing/event/CaretEvent.js
+javax/swing/event/CaretListener.js
+javax/swing/event/ChangeEvent.js
+javax/swing/event/ChangeListener.js
+javax/swing/event/DocumentEvent.js
+javax/swing/event/DocumentListener.js
+javax/swing/event/EventListenerList.js
+javax/swing/event/ListDataEvent.js
+javax/swing/event/ListDataListener.js
+javax/swing/event/UndoableEditEvent.js
+javax/swing/event/UndoableEditListener.js
+javax/swing/FocusManager.js
+javax/swing/InternalFrameFocusTraversalPolicy.js
+javax/swing/LayoutComparator.js
+javax/swing/LayoutFocusTraversalPolicy.js
+javax/swing/SortingFocusTraversalPolicy.js
+javax/swing/SwingContainerOrderFocusTraversalPolicy.js
+javax/swing/SwingDefaultFocusTraversalPolicy.js
+javax/swing/Icon.js
+javax/swing/ImageIcon.js
+javax/swing/InputMap.js
+javax/swing/JApplet.js
+javax/swing/JButton.js
+javax/swing/JCheckBox.js
+javax/swing/JCheckBoxMenuItem.js
+javax/swing/JComboBox.js
+javax/swing/JComponent.js
+javax/swing/JFrame.js
+javax/swing/JLabel.js
+javax/swing/JLayeredPane.js
+javax/swing/JMenu.js
+javax/swing/JMenuBar.js
+javax/swing/JMenuItem.js
+javax/swing/JPanel.js
+javax/swing/JPopupMenu.js
+javax/swing/JRadioButtonMenuItem.js
+javax/swing/JRootPane.js
+javax/swing/JScrollBar.js
+javax/swing/JScrollPane.js
+javax/swing/JSeparator.js
+javax/swing/JTextArea.js
+javax/swing/JTextField.js
+javax/swing/JToggleButton.js
+javax/swing/JViewport.js
+javax/swing/KeyboardManager.js
+javax/swing/KeyStroke.js
+javax/swing/ListModel.js
+javax/swing/LookAndFeel.js
+javax/swing/MenuElement.js
+javax/swing/MutableComboBoxModel.js
+javax/swing/plaf/ActionMapUIResource.js
+javax/swing/plaf/basic/BasicBorders.js
+javax/swing/plaf/BorderUIResource.js
+javax/swing/plaf/ColorUIResource.js
+javax/swing/plaf/ComponentUI.js
+javax/swing/plaf/DimensionUIResource.js
+javax/swing/plaf/FontUIResource.js
+javax/swing/plaf/InputMapUIResource.js
+javax/swing/plaf/InsetsUIResource.js
+javax/swing/plaf/UIResource.js
+javax/swing/RepaintManager.js
+javax/swing/RootPaneContainer.js
+javax/swing/Scrollable.js
+javax/swing/ScrollPaneConstants.js
+javax/swing/ScrollPaneLayout.js
+javax/swing/SingleSelectionModel.js
+javax/swing/SizeRequirements.js
+javax/swing/SwingConstants.js
+javax/swing/SwingPaintEventDispatcher.js
+javax/swing/SwingUtilities.js
+javax/swing/text/AbstractDocument.js
+javax/swing/text/AttributeSet.js
+javax/swing/text/Caret.js
+javax/swing/text/DefaultCaret.js
+javax/swing/text/DefaultEditorKit.js
+javax/swing/text/Document.js
+javax/swing/text/EditorKit.js
+javax/swing/text/Element.js
+javax/swing/text/GapContent.js
+javax/swing/text/GapVector.js
+javax/swing/text/JTextComponent.js
+javax/swing/text/MutableAttributeSet.js
+javax/swing/text/PlainDocument.js
+javax/swing/text/PlainView.js
+javax/swing/text/Position.js
+javax/swing/text/Segment.js
+javax/swing/text/SegmentCache.js
+javax/swing/text/SimpleAttributeSet.js
+javax/swing/text/Style.js
+javax/swing/text/StyleConstants.js
+javax/swing/text/StyleContext.js
+javax/swing/text/TabExpander.js
+javax/swing/text/TextAction.js
+javax/swing/text/Utilities.js
+javax/swing/text/View.js
+javax/swing/tree/TreeNode.js
+javax/swing/UIDefaults.js
+javax/swing/UIManager.js
+javax/swing/undo/AbstractUndoableEdit.js
+javax/swing/undo/CompoundEdit.js
+javax/swing/undo/UndoableEdit.js
+javax/swing/ViewportLayout.js
+javax/swing/WindowConstants.js
+sun/awt/AppContext.js
+sun/awt/AWTAutoShutdown.js
+sun/awt/CausedFocusEvent.js
+sun/awt/ComponentFactory.js
+sun/awt/KeyboardFocusManagerPeerProvider.js
+sun/awt/MostRecentKeyValue.js
+sun/awt/MostRecentThreadAppContext.js
+sun/awt/PaintEventDispatcher.js
+sun/awt/PostEventQueue.js
+sun/awt/RequestFocusController.js
+sun/awt/SunToolkit.js
+sun/awt/WindowClosingListener.js
+sun/awt/WindowClosingSupport.js
+sun/awt/image/DataStealer.js
+sun/awt/image/IntegerComponentRaster.js
+sun/awt/image/IntegerInterleavedRaster.js
+sun/awt/image/SunWritableRaster.js
+sun/font/FontDesignMetrics.js
+sun/swing/DefaultLookup.js
+sun/swing/SwingLazyValue.js
+sun/text/resources/FormatData.js
+sun/text/resources/en/FormatData_en.js
+sun/util/resources/LocaleData.js
+sun/util/locale/BaseLocale.js
+sun/util/locale/LocaleUtils.js
+sun/util/locale/provider/LocaleProviderAdapter.js
+sun/util/locale/provider/LocaleDataMetaInfo.js
+swingjs/a2s/A2SContainer.js
+swingjs/a2s/A2SEvent.js
+swingjs/a2s/A2SListener.js
+swingjs/a2s/Applet.js
+swingjs/a2s/Button.js
+swingjs/a2s/Label.js
+swingjs/a2s/Panel.js
+swingjs/a2s/Scrollbar.js
+swingjs/a2s/ScrollPane.js
+swingjs/a2s/TextArea.js
+swingjs/a2s/TextField.js
+swingjs/api/Interface.js
+swingjs/api/js/DOMNode.js
+swingjs/api/js/HTML5CanvasContext2D.js
+swingjs/api/js/JSInterface.js
+swingjs/jquery/JQueryUI.js
+swingjs/JSApp.js
+swingjs/JSAppletThread.js
+swingjs/JSAppletViewer.js
+swingjs/JSFocusPeer.js
+swingjs/JSFontMetrics.js
+swingjs/JSFrameViewer.js
+swingjs/JSGraphics2D.js
+swingjs/JSGraphicsConfiguration.js
+swingjs/JSGraphicsEnvironment.js
+swingjs/JSImage.js
+swingjs/JSImagekit.js
+swingjs/JSMouse.js
+swingjs/JSNullComponentPeer.js
+swingjs/JSScreenDevice.js
+swingjs/JSThreadGroup.js
+swingjs/JSToolkit.js
+swingjs/JSUtil.js
+swingjs/plaf/ButtonListener.js
+swingjs/plaf/DefaultMenuLayout.js
+swingjs/plaf/HTML5LookAndFeel.js
+swingjs/plaf/JSAppletUI.js
+swingjs/plaf/JSButtonUI.js
+swingjs/plaf/JSCheckBoxMenuItemUI.js
+swingjs/plaf/JSCheckBoxUI.js
+swingjs/plaf/JSComboBoxUI.js
+swingjs/plaf/JSComponentUI.js
+swingjs/plaf/JSEventHandler.js
+swingjs/plaf/JSFrameUI.js
+swingjs/plaf/JSGraphicsUtils.js
+swingjs/plaf/JSLabelUI.js
+swingjs/plaf/JSLayeredPaneUI.js
+swingjs/plaf/JSLightweightUI.js
+swingjs/plaf/JSMenuBarUI.js
+swingjs/plaf/JSMenuItemUI.js
+swingjs/plaf/JSMenuUI.js
+swingjs/plaf/JSPanelUI.js
+swingjs/plaf/JSPopupMenuSeparatorUI.js
+swingjs/plaf/JSPopupMenuUI.js
+swingjs/plaf/JSRadioButtonMenuItemUI.js
+swingjs/plaf/JSRadioButtonUI.js
+swingjs/plaf/JSRootPaneUI.js
+swingjs/plaf/JSScrollBarUI.js
+swingjs/plaf/JSScrollPaneUI.js
+swingjs/plaf/JSSeparatorUI.js
+swingjs/plaf/JSSliderUI.js
+swingjs/plaf/JSTextAreaUI.js
+swingjs/plaf/JSTextFieldUI.js
+swingjs/plaf/JSTextUI.js
+swingjs/plaf/JSTextViewUI.js
+swingjs/plaf/JSViewportUI.js
+swingjs/plaf/JSWindowUI.js
+swingjs/plaf/LazyActionMap.js
+swingjs/plaf/Resizer.js
+swingjs/plaf/TextListener.js
+
+
diff --git a/swingjs/ver/3.3.1/differences.txt b/swingjs/ver/3.3.1/differences.txt
new file mode 100644 (file)
index 0000000..c9ec027
--- /dev/null
@@ -0,0 +1,1526 @@
+java2script/SwingJS Notes
+=========================
+
+updated 12/31/2020 -- full support for 64-bit long
+updated 12/6/2020 -- note about restrictions on long, including BitSet and Scanner
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+The goal of java2script/SwingJS is NOT to reproduce Java byte code processing in a 
+browser. We leave that task and its own issues to others. Here, instead, we have a 
+working JavaScript version of the Java classes along with runtime assistance in the
+j2sClazz.js library. This design has several advantages:
+
+ 1) It leads to much smaller downloads, since the class loader can dynamically load
+    code at the class level. 
+    
+ 2) It allow the browser to use its own optimizations and features, not to ignore those. 
+    This leads to huge performance gains and in many cases much simpler coding.
+ 3) It allows for in-browser debugging and analysis. 
+ 4) It allows for code switching between Java and JavaScript. Working Java code 
+    can be annotated (@j2sNative, @j2sAlias, @j2sIgnore) in a fashion that 
+    allows the code to run slightly differently in a Java than a JavaScript environment.
+    For example:
+    
+       int delayMS = /** @j2sNative 10 ||*/2;
+    
+    will read "var delayMS = 10 || 2;"  (i.e. 10) in JavaScript but read by the Java
+    compiler as "int delayMS = 2". 
+    
+ 5) Just generally, it allows for a much more integrated environment. JavaScript on
+    the page can call into any SwingJS program, and, likewise, any SwingJS code can 
+    access anything on the page.    
+
+
+Method and Field Disambiguation
+-------------------------------
+
+This is no problem. SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. 
+
+In Java, a class and its subclass can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified, and how individual methods
+can have more than one name using @j2sAlias. 
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem.   
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+The javajs.async package can be added to any Java program to provide Java+JavaScript asynchronous
+classes, including AsyncColorChooser, AsyncDialog, AsyncFileChooser, and AsyncSwingWorker. All
+of these classes work just as well in Java as in JavaScript. There is no need to run them 
+only when in JavaScript.
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity. Most access 
+to this package in working Java should be via the swingjs.api.JSUtilI interface.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java or JavaScript equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. Basically, what we have is a Java Swing "LookAndFeel" customized for HTML.
+
+The issue here is complex but workable. In Java there are two background concepts -- the 
+Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as BasicButtonUI or BasicTextFieldUI).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. They are chunks of low-level code that
+paint the screen to give the illusion that you really are pressing a button or typing text 
+ot the screen. Their events are being passed on to Java or the browser by the operating system. 
+
+UI classes provide a consistent "look and feel" for these native objects, rendering them onto 
+the native window canvas and handling all user-generated events. They paint the borders, 
+the backgrounds, the highlights, of every control you see in Java. There is one-to-one 
+correspondence of Swing classes and UI classes. Setting the Look and Feel for a project amounts 
+to selecting the directory from which to draw these UI classes. Java's UI class interfaces can
+be found in the javax.swing.plaf ("platform look and feel") package. Individual look and feel
+implementations are found in sun.plaf.basic, sun.plaf.metal, and other such specialized packages.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. However, this is not really an
+issue. We have reconfigured the class relationships a bit. In SwingJS, all AWT components
+are now subclasses of javax.swing.JComponent. While this might seem error prone, so far we have 
+found no problem with this arrangement. It's a little surprising to me that the original developers
+of Swing did not think of this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." 
+
+Then, to tie it all togeter, all AWT components such as java.awt.Button now subclass their respective
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. 
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+files read by SwingJS applications. That is because just peeking at a file 
+in SwingJS will load its entire byte[] data. 
+Optimally, all work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. The string name can be used alone, since SwingJS will
+cache the files itself and not reload them -- just as the browser normally does.
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine. Temporary files will be placed in the 
+"/TEMP/" directory, as seen from the running Java program. Any file written to this directory will
+simply be stored in memory; files written to any other directory, when closed, will appear to the 
+user as a download, often involving a "What do you want to do with this file" dialog. 
+
+
+See below for details relating to each of the subjects below:
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+none as of 2020.12.31. Source code for classes and methods missing
+from Java 8 or from Java 9+ can be inserted by any developer along 
+with their running code source, and they should run.  
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+primitive type restrictions - int, long, and float
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+no format internationalization
+Graphics2D: missing winding rules
+text-related field implementation
+Formatter/Regex limitations
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+primitive restrictions - int, long, and float
+---------------------------------------------
+
+int
+
+For performance reasons, int addition and multiplication do not by default overflow to 
+negative values. Instead, they just get bigger. Java code that relies on overflow to 
+negative values should be surrounded by ()|0 -- an OR with integer 0:
+
+
+int bigI, bigJ;
+...
+
+bigI = (bigI + bigJ)|0;
+
+bigI = (bigI + 1)|0;   //instead of bigI++
+
+
+Thus, in Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+This is because, generally, "-1" in JavaScript is not 0xFFFFFFFF. The simple ()|0 takes
+caes of this:
+
+               int newLength = (lineBuf.length * 2)|0;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+JavaScript does process bitwise operators & | ^ ~ properly for int values. There is no issue using
+these operations. 
+
+Note that int 1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+Importantly, the JavaScript Int32Array does behave properly. From the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. 
+
+long
+
+Java's 64-bit long type is fully supported, starting with java2script 3.3.1 (2020.12.31)
+The transpiler handles all conversions to and from long appropriately. See the discussion
+at https://github.com/BobHanson/java2script/issues/202 for how this is done.
+
+float
+
+SwingJS does not distinguish between float and double. Everything is double.
+
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. That is not a misprint. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript:
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true in SwingJS:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because, in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+java.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For a full discussion of modal dialogs, see the javajs.asyc.AsyncDialog.java discussion.
+
+For this action to work, the parent component must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this call.
+
+All of the standard Java events associated with Components are also available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ only the javadoc code will run in JavaScript, and only the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. 
+
+You can implement a simple name for a method using the @j2sAlias annoation in the javadoc for the 
+method involved. For example:
+
+
+/**
+ * @j2sAlias read
+ *
+ */
+public void read(byte[] buf, int pos, int len) {...}
+
+will allow the method to be accesible either as "read" or "read$BA$I$I" in JavaScript. 
+
+
+The default situation for this is a class name includes ".api.js" (case-sensitive). 
+This means that any method in any class in a package js within a package api, or any private interface js 
+that has an outer interface api, will have all-unqualified methods. An example of this is 
+swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics) call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+      
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+sun.awt.AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger and java.math.BigDecimal are fully supported. 
+
+
+no format internationalization
+------------------------------
+
+For now, just "en" for number and date formatters
+
+
+missing winding rules
+---------------------
+
+When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+In addition, JavaScript does not implement some of the more arcane POSIX {...} formats. 
+From java.util.regex.Pattern.java, we find the listing of conversions SwingJS does use:
+
+               "\\p{javaWhitespace}","\\s",
+               "\\p{javaDigit}","\\d",
+               "\\p{Lower}", "[a-z]",
+               "\\p{Upper}", "[A-Z]",
+               "\\p{ASCII}", "[\u0000-\u007F]",
+               "\\p{Alpha}", "[A-Za-z]",
+               "\\p{Digit}", "[0-9]",
+               "\\p{Alnum}", "[A-Za-z0-9]",
+               "\\p{Punct}", "[!\"#$%&'\\(\\)\\*\\+,-./:;<=>?@\\[\\\\\\]^_`{\\|}~]",
+               "\\p{Graph}", "[A-Za-z0-9]!\"#$%&'\\(\\)\\*\\+,-./:;<=>?@\\[\\\\\\]^_`{\\|}~]",
+               "\\p{Print}", "[A-Za-z0-9]!\"#$%&'\\(\\)\\*\\+,-./:;<=>?@\\[\\\\\\]^_`{\\|}~]",
+               "\\p{Blank}", "[ \t]",
+               "\\p{Cntrl}", "[\u0000-\u001F\u007F]",
+               "\\p{XDigit}", "[0-9a-fA-F]",
+               "\\p{Space}", "[ \t\n\u000B\f\r]",
+               "\\p{javaLowerCase}", "[a-z]",
+               "\\p{javaUpperCase}", "[A-Z]",
+               "\\p{Sc}", "[\u0024\u00A2\u00A3\u00A4\u00A5\u058F\u060B\u07FE\u07FF\u09F2\u09F3\u09FB\u0AF1\u0BF9\u0E3F\u20A0\u20A1\u20A2\u20A3\u20A4\u20A5\u20A6\u20A7\u20A8\u20A9\u20AA\u20AB\u20AC\u20AD\u20AE\u20AF\u20B0\u20B1\u20B2\u20B3\u20B4\u20B5\u20B6\u20B7\u20B8\u20B9\u20BA\u20BB\u20BC\u20BD\u20BE\u20BF\uA838\uFDFC\uFE69\uFF04\uFFE0\uFFE1\uFFE5\uFFE6]"
+
+Java's \Q \E quoting is handled appropriately.
+
+Additional Issues
+-----------------
+
+Method reflection is limited. Fields and methods do not retain public or default characteristics. 
+(This could be easily adapted, though.) Interfaces do not expose their methods, as the transpiler does not 
+actually transpile the interfaces themselves unless they contain default methods. 
+And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+
+
diff --git a/swingjs/ver/3.3.1/net.sf.j2s.core.jar b/swingjs/ver/3.3.1/net.sf.j2s.core.jar
new file mode 100644 (file)
index 0000000..ced2ae7
Binary files /dev/null and b/swingjs/ver/3.3.1/net.sf.j2s.core.jar differ
diff --git a/swingjs/ver/3.3.1/timestamp b/swingjs/ver/3.3.1/timestamp
new file mode 100644 (file)
index 0000000..204bf8d
--- /dev/null
@@ -0,0 +1 @@
+20210728172208 
diff --git a/swingjs/ver/pre-long/DEV_NOTES.txt b/swingjs/ver/pre-long/DEV_NOTES.txt
new file mode 100644 (file)
index 0000000..751d81c
--- /dev/null
@@ -0,0 +1,10 @@
+This is sources/net.sf.j2s.java.core/dist/DEV_NOTES.txt
+
+_j2sclasslist.txt 
+
+the list of .js files concatenated into coreswingjs.js and minified to coreswingjs.z.js
+
+
+SwingJS-site.zip
+
+the full site directory for SwingJS including all files not in the test/ directory.
diff --git a/swingjs/ver/pre-long/README-pre-long.txt b/swingjs/ver/pre-long/README-pre-long.txt
new file mode 100644 (file)
index 0000000..e0c6163
--- /dev/null
@@ -0,0 +1,17 @@
+This directory holds 3.2.9 transpiler and runtime tagged previous 
+to the transpiler fix for boxed number direct manipulation:
+
+Long L = Long.valueOf(3);
+L++;
+L /= 2;
+L += 10;
+L = L / 5;
+
+etc. 
+
+These assignments are not made properly and fail. 
+
+In addition, this runtime does not handle Long properly and also had an issue with Short.equals(Object)
+
+just saving this and will probably delete it. 
+
diff --git a/swingjs/ver/pre-long/SwingJS-site.zip b/swingjs/ver/pre-long/SwingJS-site.zip
new file mode 100644 (file)
index 0000000..73853d4
Binary files /dev/null and b/swingjs/ver/pre-long/SwingJS-site.zip differ
diff --git a/swingjs/ver/pre-long/_j2sclasslist.txt b/swingjs/ver/pre-long/_j2sclasslist.txt
new file mode 100644 (file)
index 0000000..076f300
--- /dev/null
@@ -0,0 +1,412 @@
+java/applet/Applet.js
+java/applet/AppletContext.js
+java/applet/AppletStub.js
+java/applet/JSApplet.js
+java/awt/ActiveEvent.js
+java/awt/Adjustable.js
+java/awt/AWTEvent.js
+java/awt/AWTEventMulticaster.js
+java/awt/AWTKeyStroke.js
+java/awt/BasicStroke.js
+java/awt/BorderLayout.js
+java/awt/Button.js
+java/awt/Color.js
+java/awt/color/ColorSpace.js
+java/awt/Component.js
+java/awt/ComponentOrientation.js
+java/awt/ContainerOrderFocusTraversalPolicy.js
+java/awt/Container.js
+java/awt/Cursor.js
+java/awt/DefaultFocusTraversalPolicy.js
+java/awt/DefaultKeyboardFocusManager.js
+java/awt/Dialog.js
+java/awt/Dimension.js
+java/awt/dnd/peer/DropTargetPeer.js
+java/awt/event/ActionListener.js
+java/awt/event/AdjustmentEvent.js
+java/awt/event/AdjustmentListener.js
+java/awt/event/AWTEventListener.js
+java/awt/event/ComponentAdapter.js
+java/awt/event/ComponentEvent.js
+java/awt/event/ComponentListener.js
+java/awt/event/ContainerListener.js
+java/awt/event/FocusEvent.js
+java/awt/event/FocusListener.js
+java/awt/event/HierarchyBoundsListener.js
+java/awt/event/HierarchyListener.js
+java/awt/event/InputEvent.js
+java/awt/event/InputMethodListener.js
+java/awt/event/InvocationEvent.js
+java/awt/event/ItemEvent.js
+java/awt/event/ItemListener.js
+java/awt/event/KeyListener.js
+java/awt/event/MouseEvent.js
+java/awt/event/MouseListener.js
+java/awt/event/MouseMotionListener.js
+java/awt/event/MouseWheelListener.js
+java/awt/event/TextListener.js
+java/awt/event/WindowAdapter.js
+java/awt/event/WindowEvent.js
+java/awt/event/WindowFocusListener.js
+java/awt/event/WindowListener.js
+java/awt/event/WindowStateListener.js
+java/awt/EventDispatchThread.js
+java/awt/EventFilter.js
+java/awt/EventQueue.js
+java/awt/EventQueueItem.js
+java/awt/FlowLayout.js
+java/awt/FocusTraversalPolicy.js
+java/awt/Font.js
+java/awt/font/FontRenderContext.js
+java/awt/FontMetrics.js
+java/awt/Frame.js
+java/awt/geom/AffineTransform.js
+java/awt/geom/Dimension2D.js
+java/awt/geom/Path2D.js
+java/awt/geom/PathIterator.js
+java/awt/geom/Point2D.js
+java/awt/geom/Rectangle2D.js
+java/awt/geom/RectangularShape.js
+java/awt/geom/RectIterator.js
+java/awt/GraphicsCallback.js
+java/awt/GraphicsConfiguration.js
+java/awt/GraphicsDevice.js
+java/awt/GraphicsEnvironment.js
+java/awt/Image.js
+java/awt/image/ImageObserver.js
+java/awt/Insets.js
+java/awt/ItemSelectable.js
+java/awt/JSComponent.js
+java/awt/JSDialog.js
+java/awt/JSFrame.js
+java/awt/JSPanel.js
+java/awt/KeyboardFocusManager.js
+java/awt/KeyEventDispatcher.js
+java/awt/KeyEventPostProcessor.js
+java/awt/Label.js
+java/awt/LayoutManager.js
+java/awt/LayoutManager2.js
+java/awt/LightweightDispatcher.js
+java/awt/Paint.js
+java/awt/Panel.js
+java/awt/peer/ComponentPeer.js
+java/awt/peer/ContainerPeer.js
+java/awt/peer/FramePeer.js
+java/awt/peer/KeyboardFocusManagerPeer.js
+java/awt/peer/LightweightPeer.js
+java/awt/peer/WindowPeer.js
+java/awt/Point.js
+java/awt/Queue.js
+java/awt/Rectangle.js
+java/awt/RenderingHints.js
+java/awt/Scrollbar.js
+java/awt/ScrollPane.js
+java/awt/Shape.js
+java/awt/Stroke.js
+java/awt/TextArea.js
+java/awt/TextComponent.js
+java/awt/TextField.js
+java/awt/Toolkit.js
+java/awt/Transparency.js
+java/awt/Window.js
+java/beans/ChangeListenerMap.js
+java/beans/PropertyChangeEvent.js
+java/beans/PropertyChangeListener.js
+java/beans/PropertyChangeSupport.js
+java/lang/AbstractStringBuilder.js
+java/lang/Class.js
+java/lang/Enum.js
+java/lang/Iterable.js
+java/lang/reflect/Constructor.js
+java/lang/reflect/Method.js
+java/lang/StringBuffer.js
+java/lang/StringBuilder.js
+java/lang/Thread.js
+java/lang/ThreadGroup.js
+java/math/RoundingMode.js
+java/net/URL.js
+java/net/URLStreamHandlerFactory.js
+java/net/HttpURLConnection.js
+java/net/URLStreamHandler.js
+javax/net/ssl/HttpsUrlConnection.js
+java/text/CharacterIterator.js
+java/text/DecimalFormat.js
+java/text/DecimalFormatSymbols.js
+java/text/DigitList.js
+java/text/FieldPosition.js
+java/text/Format.js
+java/text/NumberFormat.js
+java/util/AbstractCollection.js
+java/util/AbstractList.js
+java/util/AbstractMap.js
+java/util/AbstractSequentialList.js
+java/util/AbstractSet.js
+java/util/ArrayList.js
+java/util/Arrays.js
+java/util/Collection.js
+java/util/Collections.js
+java/util/Comparator.js
+java/util/Deque.js
+java/util/Dictionary.js
+java/util/Enumeration.js
+java/util/EventListener.js
+java/util/EventObject.js
+java/util/HashMap.js
+java/util/HashSet.js
+java/util/Hashtable.js
+java/util/IdentityHashMap.js
+java/util/Iterator.js
+java/util/LinkedHashMap.js
+java/util/LinkedList.js
+java/util/List.js
+java/util/ListResourceBundle.js
+java/util/Locale.js
+java/util/Map.js
+java/util/Objects.js
+java/util/Queue.js
+java/util/Random.js
+java/util/RandomAccess.js
+java/util/ResourceBundle.js
+java/util/Set.js
+java/util/TimSort.js
+java/util/Vector.js
+javajs/api/JSFunction.js
+javajs/util/AjaxURLConnection.js
+javajs/util/AjaxURLStreamHandlerFactory.js
+javajs/util/AU.js
+javajs/util/JSThread.js
+javajs/util/Lst.js
+javajs/util/PT.js
+javajs/util/Rdr.js
+javajs/util/SB.js
+javax/swing/AbstractAction.js
+javax/swing/AbstractButton.js
+javax/swing/AbstractListModel.js
+javax/swing/Action.js
+javax/swing/ActionMap.js
+javax/swing/AncestorNotifier.js
+javax/swing/ArrayTable.js
+javax/swing/border/AbstractBorder.js
+javax/swing/border/BevelBorder.js
+javax/swing/border/Border.js
+javax/swing/border/CompoundBorder.js
+javax/swing/border/EmptyBorder.js
+javax/swing/border/EtchedBorder.js
+javax/swing/border/LineBorder.js
+javax/swing/border/TitledBorder.js
+javax/swing/BorderFactory.js
+javax/swing/BoundedRangeModel.js
+javax/swing/BoxLayout.js
+javax/swing/ButtonGroup.js
+javax/swing/ButtonModel.js
+javax/swing/ClientPropertyKey.js
+javax/swing/ComboBoxModel.js
+javax/swing/DefaultBoundedRangeModel.js
+javax/swing/DefaultButtonModel.js
+javax/swing/DefaultComboBoxModel.js
+javax/swing/DefaultSingleSelectionModel.js
+javax/swing/DropMode.js
+javax/swing/event/AncestorEvent.js
+javax/swing/event/AncestorListener.js
+javax/swing/event/CaretEvent.js
+javax/swing/event/CaretListener.js
+javax/swing/event/ChangeEvent.js
+javax/swing/event/ChangeListener.js
+javax/swing/event/DocumentEvent.js
+javax/swing/event/DocumentListener.js
+javax/swing/event/EventListenerList.js
+javax/swing/event/ListDataEvent.js
+javax/swing/event/ListDataListener.js
+javax/swing/event/UndoableEditEvent.js
+javax/swing/event/UndoableEditListener.js
+javax/swing/FocusManager.js
+javax/swing/InternalFrameFocusTraversalPolicy.js
+javax/swing/LayoutComparator.js
+javax/swing/LayoutFocusTraversalPolicy.js
+javax/swing/SortingFocusTraversalPolicy.js
+javax/swing/SwingContainerOrderFocusTraversalPolicy.js
+javax/swing/SwingDefaultFocusTraversalPolicy.js
+javax/swing/Icon.js
+javax/swing/ImageIcon.js
+javax/swing/InputMap.js
+javax/swing/JApplet.js
+javax/swing/JButton.js
+javax/swing/JCheckBox.js
+javax/swing/JCheckBoxMenuItem.js
+javax/swing/JComboBox.js
+javax/swing/JComponent.js
+javax/swing/JFrame.js
+javax/swing/JLabel.js
+javax/swing/JLayeredPane.js
+javax/swing/JMenu.js
+javax/swing/JMenuBar.js
+javax/swing/JMenuItem.js
+javax/swing/JPanel.js
+javax/swing/JPopupMenu.js
+javax/swing/JRadioButtonMenuItem.js
+javax/swing/JRootPane.js
+javax/swing/JScrollBar.js
+javax/swing/JScrollPane.js
+javax/swing/JSeparator.js
+javax/swing/JTextArea.js
+javax/swing/JTextField.js
+javax/swing/JToggleButton.js
+javax/swing/JViewport.js
+javax/swing/KeyboardManager.js
+javax/swing/KeyStroke.js
+javax/swing/ListModel.js
+javax/swing/LookAndFeel.js
+javax/swing/MenuElement.js
+javax/swing/MutableComboBoxModel.js
+javax/swing/plaf/ActionMapUIResource.js
+javax/swing/plaf/basic/BasicBorders.js
+javax/swing/plaf/BorderUIResource.js
+javax/swing/plaf/ColorUIResource.js
+javax/swing/plaf/ComponentUI.js
+javax/swing/plaf/DimensionUIResource.js
+javax/swing/plaf/FontUIResource.js
+javax/swing/plaf/InputMapUIResource.js
+javax/swing/plaf/InsetsUIResource.js
+javax/swing/plaf/UIResource.js
+javax/swing/RepaintManager.js
+javax/swing/RootPaneContainer.js
+javax/swing/Scrollable.js
+javax/swing/ScrollPaneConstants.js
+javax/swing/ScrollPaneLayout.js
+javax/swing/SingleSelectionModel.js
+javax/swing/SizeRequirements.js
+javax/swing/SwingConstants.js
+javax/swing/SwingPaintEventDispatcher.js
+javax/swing/SwingUtilities.js
+javax/swing/text/AbstractDocument.js
+javax/swing/text/AttributeSet.js
+javax/swing/text/Caret.js
+javax/swing/text/DefaultCaret.js
+javax/swing/text/DefaultEditorKit.js
+javax/swing/text/Document.js
+javax/swing/text/EditorKit.js
+javax/swing/text/Element.js
+javax/swing/text/GapContent.js
+javax/swing/text/GapVector.js
+javax/swing/text/JTextComponent.js
+javax/swing/text/MutableAttributeSet.js
+javax/swing/text/PlainDocument.js
+javax/swing/text/PlainView.js
+javax/swing/text/Position.js
+javax/swing/text/Segment.js
+javax/swing/text/SegmentCache.js
+javax/swing/text/SimpleAttributeSet.js
+javax/swing/text/Style.js
+javax/swing/text/StyleConstants.js
+javax/swing/text/StyleContext.js
+javax/swing/text/TabExpander.js
+javax/swing/text/TextAction.js
+javax/swing/text/Utilities.js
+javax/swing/text/View.js
+javax/swing/tree/TreeNode.js
+javax/swing/UIDefaults.js
+javax/swing/UIManager.js
+javax/swing/undo/AbstractUndoableEdit.js
+javax/swing/undo/CompoundEdit.js
+javax/swing/undo/UndoableEdit.js
+javax/swing/ViewportLayout.js
+javax/swing/WindowConstants.js
+sun/awt/AppContext.js
+sun/awt/AWTAutoShutdown.js
+sun/awt/CausedFocusEvent.js
+sun/awt/ComponentFactory.js
+sun/awt/KeyboardFocusManagerPeerProvider.js
+sun/awt/MostRecentKeyValue.js
+sun/awt/MostRecentThreadAppContext.js
+sun/awt/PaintEventDispatcher.js
+sun/awt/PostEventQueue.js
+sun/awt/RequestFocusController.js
+sun/awt/SunToolkit.js
+sun/awt/WindowClosingListener.js
+sun/awt/WindowClosingSupport.js
+sun/awt/image/DataStealer.js
+sun/awt/image/IntegerComponentRaster.js
+sun/awt/image/IntegerInterleavedRaster.js
+sun/awt/image/SunWritableRaster.js
+sun/font/FontDesignMetrics.js
+sun/swing/DefaultLookup.js
+sun/swing/SwingLazyValue.js
+sun/text/resources/FormatData.js
+sun/text/resources/en/FormatData_en.js
+sun/util/resources/LocaleData.js
+sun/util/locale/BaseLocale.js
+sun/util/locale/LocaleUtils.js
+sun/util/locale/provider/LocaleProviderAdapter.js
+sun/util/locale/provider/LocaleDataMetaInfo.js
+swingjs/a2s/A2SContainer.js
+swingjs/a2s/A2SEvent.js
+swingjs/a2s/A2SListener.js
+swingjs/a2s/Applet.js
+swingjs/a2s/Button.js
+swingjs/a2s/Label.js
+swingjs/a2s/Panel.js
+swingjs/a2s/Scrollbar.js
+swingjs/a2s/ScrollPane.js
+swingjs/a2s/TextArea.js
+swingjs/a2s/TextField.js
+swingjs/api/Interface.js
+swingjs/api/js/DOMNode.js
+swingjs/api/js/HTML5CanvasContext2D.js
+swingjs/api/js/JSInterface.js
+swingjs/jquery/JQueryUI.js
+swingjs/JSApp.js
+swingjs/JSAppletThread.js
+swingjs/JSAppletViewer.js
+swingjs/JSFocusPeer.js
+swingjs/JSFontMetrics.js
+swingjs/JSFrameViewer.js
+swingjs/JSGraphics2D.js
+swingjs/JSGraphicsConfiguration.js
+swingjs/JSGraphicsEnvironment.js
+swingjs/JSImage.js
+swingjs/JSImagekit.js
+swingjs/JSMouse.js
+swingjs/JSNullComponentPeer.js
+swingjs/JSScreenDevice.js
+swingjs/JSThreadGroup.js
+swingjs/JSToolkit.js
+swingjs/JSUtil.js
+swingjs/plaf/ButtonListener.js
+swingjs/plaf/DefaultMenuLayout.js
+swingjs/plaf/HTML5LookAndFeel.js
+swingjs/plaf/JSAppletUI.js
+swingjs/plaf/JSButtonUI.js
+swingjs/plaf/JSCheckBoxMenuItemUI.js
+swingjs/plaf/JSCheckBoxUI.js
+swingjs/plaf/JSComboBoxUI.js
+swingjs/plaf/JSComponentUI.js
+swingjs/plaf/JSEventHandler.js
+swingjs/plaf/JSFrameUI.js
+swingjs/plaf/JSGraphicsUtils.js
+swingjs/plaf/JSLabelUI.js
+swingjs/plaf/JSLayeredPaneUI.js
+swingjs/plaf/JSLightweightUI.js
+swingjs/plaf/JSMenuBarUI.js
+swingjs/plaf/JSMenuItemUI.js
+swingjs/plaf/JSMenuUI.js
+swingjs/plaf/JSPanelUI.js
+swingjs/plaf/JSPopupMenuSeparatorUI.js
+swingjs/plaf/JSPopupMenuUI.js
+swingjs/plaf/JSRadioButtonMenuItemUI.js
+swingjs/plaf/JSRadioButtonUI.js
+swingjs/plaf/JSRootPaneUI.js
+swingjs/plaf/JSScrollBarUI.js
+swingjs/plaf/JSScrollPaneUI.js
+swingjs/plaf/JSSeparatorUI.js
+swingjs/plaf/JSSliderUI.js
+swingjs/plaf/JSTextAreaUI.js
+swingjs/plaf/JSTextFieldUI.js
+swingjs/plaf/JSTextUI.js
+swingjs/plaf/JSTextViewUI.js
+swingjs/plaf/JSViewportUI.js
+swingjs/plaf/JSWindowUI.js
+swingjs/plaf/LazyActionMap.js
+swingjs/plaf/Resizer.js
+swingjs/plaf/TextListener.js
+
+
diff --git a/swingjs/ver/pre-long/differences.txt b/swingjs/ver/pre-long/differences.txt
new file mode 100644 (file)
index 0000000..60f5fcc
--- /dev/null
@@ -0,0 +1,1541 @@
+Notes
+=====
+
+---IMPORTANT CHARACTER SET NOTE---
+
+It is critical that all development work in Java2Script 
+be done in UTF-8. This means:
+
+- making sure your Eclipse project is set up for UTF-8 (not the Eclipse default?)
+- making sure your server can serve up UTF-8 by default for any browser-loaded files
+- making sure you don't edit a Java2Script class file or one of the site .js files
+    using a non-UTF-8 editor. It may replace non-Latin characters with "?" or garbage.
+- making sure that your web pages are delivered with proper headings indicating HTML5 and UTF-8
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+
+Note that the DOCTYPE tag is critical for some browsers to switch into HTML5 mode. (MSIE?)
+
+
+
+  
+In particular, the Mandarin character ç§˜ (mi; "secret") is used extensively throughout
+the SwingJS class files to distinguish j2s-specific fields and methods that must not 
+ever be shadowed or overridden by subclasses. For example, we see in java.lang.Thread.java:
+
+               public static JSThread ç§˜thisThread;
+
+----------------------------------
+
+
+updated 12/6/2020 -- note about restrictions on long, including BitSet and Scanner
+updated 3/21/2020 -- adds note about HashMap, Hashtable, and HashSet iterator ordering
+updated 3/20/2020 -- adds note about interning, new String("xxx"), and "xxx"
+updated 2/26/2020 -- adds Graphics.setClip issue
+updated 12/22/19 -- additional issues
+updated 11/03/19 -- adds information about File.exists() and points to src/javajs/async
+updated 10/26/19 -- adds information about File.createTempFile()
+updated 8/16/19 -- minor typos and added summary paragraph
+updated 7/19/19 -- clarification that AWT and Swing classes are supported directly
+updated 5/13/19 -- Mandarin U+79D8 reserved character; Missing Math methods; int and long
+updated 5/10/19 -- adds a section on static issues in multi-(duplicate)-applet pages
+updated 1/4/19 -- nio
+updated 9/15/18 -- adds integer 1/0 == Infinity
+updated 7/24/18 -- most classes replaced with https://github.com/frohoff/jdk8u-jdk
+updated 6/5/17 -- reserved package name "window"
+updated 3/11/17 -- myClass.getField
+updated 3/7/17 -- overloading of JSplitPane.setDividerLocation
+updated 3/2/17 -- more indication of classes not implemented (KeyListener)
+
+=============================================================================
+SwingJS and OpenJDK 8+
+=============================================================================
+
+SwingJS implements a wide range of the Java language in JavaScript. The base
+version for this implementation is OpenJDK8. some classes are implemented using 
+older source code, and there are some missing methods. For the most part, this is 
+no real problem. You can add or modify any java class just be adding it as source
+in your project. Or (preferably) you can contact me, and I can get it into the 
+distribution. Or (even more preferably) you can do that via a patch submission. 
+
+=================
+DESIGN PHILOSOPHY
+=================
+
+The java2script/SwingJS design goal is to recreate a recognizable, easily debuggable
+equivalent in JavaScript for as much of Java as practical. This means, for example, 
+that one can call in JavaScript 
+
+  new java.util.Hashtable()
+  
+and for all practical purposes it will appear that Java is running.
+
+
+Method and Field Disambiguation
+-------------------------------
+
+SwingJS has no problem with the overloading of methods, for example:
+
+  public void print(int b);
+  public void print(float b);
+
+JavaScript does not allow overloading of methods, and the common practice in
+Java of naming a field the same as a method -- isAllowed and isAllowed() -- is
+not possible in JavaScript. As a result, SwingJS implements "fully-qualified" 
+method names using "$" parameter type separation. Thus, these methods in SwingJS
+will be referred to as print$I and print$F. The rules for this encoding are
+relatively simple: 
+
+1. The seven primitive types in Java are encoded $I (int), $L (long), $F (float), 
+$D (double), $B (byte) $Z (boolean), and $H (short). 
+
+2. String and Object are encoded as $S and $O, respectively.
+
+3. "java_lang_" is dropped for all other classes in the java.lang package (as in Java).
+   For example:  $StringBuffer, not $java_lang_StringBuffer
+
+4. All other classes are encoded as 
+
+ "$" + Class.getName().replace(".","_")
+
+For example, in Java we see:
+
+  public void equals(Object o) {...}
+
+Whereas in SwingJS we have:
+
+  Clazz.newMeth(C$, 'equals$O', function (o) {...}
+
+And 
+
+ this.getContentPane().add(bar, "North");
+
+becomes
+
+ this.getContentPane$().add$java_awt_Component$O(bar, "North");
+
+5. Arrays are indicated with appended "A" for each level. So
+
+  setDataVector(Object[][] dataVector, Object[] columnIdentifiers)
+  
+becomes
+
+  setDataVector$OAA$OA(dataVector, columnIdentifiers)
+
+(It is recognized that this design does introduce a bit of ambiguity, in that
+ in principal there could be user class named XA and X in the same package,
+ and methods a(X[]) and a(XA) in the same class that cannot be distinguished.
+ The benefit of this simple system, however, triumphed over the unlikelyhood
+ of that scenario.) The transpiler could be set to flag this possibility.
+
+6. Constructors are prepended with "c$". So 
+
+  public JLabel(String text) {...}
+  
+becomes:
+
+  Clazz.newMeth(C$, 'c$$S', function (text) {...});
+
+Field disambiguation involves prepending. In Java, a class and its subclass 
+can both have the same field name, such as 
+
+ boolean visible;
+When this happens, it is called "shadowing", and though not recommended, Java allows
+it. The Java2Script transpiler will prepend such shadowing fields with "$" so that the
+subclass instance has both "visible" (for use in its methods inherited from its
+superclass) and "$visible" (for its own methods). Thus, we might see in Java:
+
+  this.visible = super.visible;
+  
+while in SwingJS we will see:
+
+  this.$visible=this.visible;
+
+since JavaScript does not have the "super" keyword.
+
+
+
+Parameterless methods such as toString() are appended with "$" to become toString$().
+The one exception to this rule is private methods, which are saved in (truly) private 
+array in the class (and are not accessible by reflection). Private parameterless 
+methods retain their simple Java name, since they cannot conflict with field names.
+
+This renaming of methods has a few consequences, which are discussed more fully below.
+See particularly the section on "qualified field and method names", where it is described
+how you can use packages or classes or interfaces with ".api.js" in them to represent JavaScript
+objects for which all method names are to be left unqualified. Note that it is not 
+possible to cherry-pick methods to be unqualified; only full packages, classes or 
+interfaces can hold this status.
+
+The swingjs.api.js package in particular contains a number of useful interfaces that
+you can import into your project for JavaScript-specific capabilities.
+
+
+Applet vs. Application
+----------------------
+
+One of the very cool aspects of SwingJS is that it doesn't particularly matter if a browser-based
+Java app is an "applet" or an "application". We don't need JNLP (Java Network Launch Protocol) 
+because now we can just start up any Java application in a browser just as easily as any applet.
+The associative array that passes information to the SwingJS applet (information that formerly
+might have been part of the APPLET tag, such as width, height, and codebase, always referred to 
+in our writing as "the Info array") allows the option to specify the JApplet/Applet "code" 
+class or the application "main" class. Either one will run just fine.
+
+
+Performance
+-----------
+
+Obviously, there are limitations. One is performance, but we have seen reproducible 
+performance at 1/6 - 1/3 the speed of Java. Achieving this performance may require
+some refactoring of the Java to make it more efficient in both Java and JavaScript. 
+"for" loops need to be more carefully crafted; use of "new" and "instanceof" need to be
+minimized in critical areas. Note that method overloading -- that is, the same method name
+with different parameters, such as read(int) and read(byte) -- is no longer any problem. 
+  
+
+Threads
+-------
+
+Although there is only a single thread in JavaScript, meaning Thread.wait(), Thread.sleep(int) and 
+Thread.notify() cannot be reproduced, we have found that this is not a serious limitation. 
+For example, javax.swing.Timer() works perfectly in JavaScript. All it means is that threads 
+that use sleep(int) or notify() must be refactored to allow Timer-like callbacks. That is, 
+they must allow full exit and re-entry of Thread.run(), not the typical while/sleep motif. 
+
+The key is to create a state-based run() that can be exited and re-entered in JavaScript.
+
+
+Static fields
+-------------
+
+Final static primitive "constant" fields (String, boolean, int, etc.) such as 
+
+static final int TEST = 3;
+static final String MY_STRING = "my " + "string";
+
+are converted to their primitive form automatically by the Eclipse Java compiler 
+and do not appear in the JavaScript by their names. 
+
+Other static fields are properties of their class and can be used as expected.
+
+Note, however, that SwingJS runs all "Java" code on a page in a common "jvm" 
+(like older versions of Java). So, like the older Java schema, the JavaScript 
+equivalents of both applets and applications will share all of their static 
+fields and methods. This includes java.lang.System. 
+
+Basically, SwingJS implementations of Java run in a browser page-based sandbox 
+instead of an applet-specific one.
+
+In general, this is no problem. But if we are to implement pages with 
+multiple applets present, we must be sure to only have static references 
+that are "final" or specifically meant to be shared in a JavaScript 
+environment only (since they will not be shared in Java).
+
+A simple solution, if static non-constant references are needed, is to attach the 
+field to Thread.currentThread.threadGroup(), which is an applet-specific reference.
+Be sure, if you do this, that you use explicit setters and getters:
+
+For example, 
+
+private static String myvar;
+
+...
+
+public void setMyVar(String x) {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative g._myvar = x;
+   * 
+   */
+   {
+     myvar = x;
+   }
+}
+
+public String getMyVar() {
+  ThreadGroup g = Thread.currentThread().threadGroup();
+  /**
+   * @j2sNative return g._myvar || null;
+   * 
+   */
+   {
+     return myvar;
+   }
+}
+ in Java will get and set x the same in JavaScript and in Java. 
+A convenient way to do this in general is to supply a singleton class with
+explicitly private-only constructors and then refer to it in Java and in JavaScript
+instead of using static field, referring to myclass.getIntance().xxx instead of 
+myclass.xxx in Java (and JavaScript). 
+
+This was done extensively in the Jalview project. See jalview.bin.Instance.
+
+
+Helper Packages -- swingjs/ and javajs/
+---------------------------------------
+
+The SwingJS library is the swingjs/ package. There are interfaces that may be of assistance
+in swingjs/api, but other than that, it is not recommended that developers access classes in 
+this package. The "public" nature of their methods is really an internal necessity.
+
+In addition to swingjs/, though, there are several useful classes in the javajs/ package
+that could be very useful. This package is a stand-alone package that can be 
+cloned in any Java project that also would be great to have in any JavaScript project
+-- SwingJS-related or not. Functionality ranges from reading and writing various file 
+formats, including PDF, BMP, PNG, GIF, JPG, JSON, ZIP, and CompoundDocument formats.
+
+A variety of highly efficient three- and four-dimensional point, vector, matrix, and 
+quaternion classes are included, as they were developed for JSmol and inherited from that
+project. 
+
+Of particular interest should be javajs/async/, which includes
+
+javajs.async.Async
+javajs.async.AsyncColorChooser
+javajs.async.AsyncDialog
+javajs.async.AsyncFileChooser
+
+See javajs.async.Async JavaDoc comments for a full description of 
+these useful classes.
+
+
+Modal Dialogs
+-------------
+
+Although true modal dialogs are not possible with only one thread, a functional equivalent -- 
+asynchronous modal dialogs -- is relatively easy to set up. All the JOptionPane dialogs will
+return PropertyChangeEvents to signal that they have been disposed of and containing the results. 
+See below and classes in the javajs.async package.
+
+
+Native calls
+------------
+
+Native calls in Java are calls to operating system methods that are not in Java. JavaScript
+has no access to these, of course, and they must all be replaced by JavaScript equivalents.
+Fortunately, they are not common, and those that are present in Java (for example, in calculating
+checksums in ZIP file creation) are at a low enough level that most developers do not utilize them
+or do not even have access to them. All native calls in Java classes have been replaced by 
+Java equivalents.
+
+
+Swing GUI Peers and UIClasses
+-----------------------------
+
+One of the biggest adaptations introduced in SwingJS is in the area of the graphical 
+user interface. The issue here is complex but workable. In Java there are two background 
+concepts -- the Component "peer" (one per "heavy-weight" component, such as a Frame) and the 
+component "uiClass" (one per component, such as JButton or JTextField).
+
+Peers are native objects of the operating system. These are the virtual buttons and text areas
+that the user is interacting with at a very base level. Their events are being passed on to 
+Java or the browser by the operating system. UI classes provide a consistent "look and feel" 
+for these native objects, rendering them onto the native window canvas and handling all 
+user-generated events. They paint the borders, the backgrounds, the highlights, of every 
+control you see in Java. There is one-to-one correspondence of Swing classes and UI classes. 
+Setting the Look and Feel for a project amounts to selecting the directory from which to draw 
+these UI classes. The UI classes can be found in the javax.swing.plaf ("platform look and feel") 
+package.
+
+Early on in the development of SwingJS, we decided not to fully reproduce the painfully detailed 
+bit-by-bit painting of controls as is done in Java. Instead, we felt it was wiser to utilize the standard
+HTML5 UI capabilities as much as possible, using DIV, and INPUT especially, with extensive use
+of CSS and sometimes jQuery (menus, and sliders, for example). Thus, we have created a new 
+set of UIs -- the "HTML5 Look and Feel". These classes can be found in swingjs.plaf. Besides being
+more adaptable, this approach allows far more versatility to SwingJS developers, allowing them
+to modify the GUI to suit their needs if desired.
+
+In SwingJS, since we have no access to native peers except through the browser DOM,
+it seemed logical to merge the peer and UI idea. So instead of having one peer per heavy-weight control and
+one UI class instance for each control type, we just have one UI class instance per control, and
+that UI class instance is what is being referred to when a "peer" is notified. 
+
+In some ways this is a throw back to when all of Swing's components were subclasses of
+specific AWT components such as Button and List. These "heavy-weight components" all had their 
+own individual native peers and thus automatically took on the look and feel provided by the OS. 
+Later Swing versions implemented full look and feel for all peers, leaving only JDialog, JFrame,
+and a few other classes to have native peers. But in SwingJS we have again a 1:1 map of component
+and UI class/peer instance.
+
+The origin of most issues (read "bugs") in relation to the GUI will probably be found in the
+swingjs.plaf JSxxxxUI.java code.
+
+  
+Swing-only Components -- no longer an issue
+-------------------------------------------
+
+Swing was introduced into Java well after the Java Abstract Window Toolkit (AWT) was well
+established. As such, its designers chose to allow AWT controls such as Button and List to be used 
+alongside their Swing counterparts JButton and JList. Reading the code, it is clear that this 
+design choice posed a huge headache for Swing class developers. 
+
+For SwingJS, we decided from the beginning NOT to allow this mixed-mode programming and 
+instead to require that all components be Swing components. 
+
+However, this is no longer an issue. All AWT components in SwingJS are now subclasses of 
+javax.swing.JComponent. So far, we have found no problem with this.
+
+The a2s Adapter Package
+-----------------------
+
+Originally, we thought that we would restrict ourselves to JApplets only. That is, only
+Swing-based applets. But as we worked, we discovered that there are a lot of great 
+applets out there that are pre-Swing pure-AWT java.applet.Applet applets. Our problem was 
+that we also wanted it to be possible to quickly adapt these applets to JavaScript as well.
+The solution turned out to be simple: Write a package (a2s) that recreates the interface for 
+non-Swing components as subclasses of Swing components. Thus, a2s.Button subclasses javax.swing.JButton
+but also accepts all of the methods of java.awt.Button. This works amazingly well, with a few
+special adaptations to the core javax.swing to be "AWT-aware." All AWT components now subclass 
+a2s components, which in turn subclass JComponents. So no changes in code are necessary. We have
+successfully transpiled over 500 applets using this strategy. (Kind of surprising, actually, that
+the original Java developers did not see that option. But we have a hindsight advantage here.)
+
+
+Working with Files
+==================
+
+Simple String file names are not optimal for passing information about
+read files within SwingJS applications. 
+All work with files should either use Path or File objects exclusively. 
+These objects, after a file is read or checked for existence, will already 
+contain the file byte[] data. Doing something like this:
+
+File f = File("./test.dat");
+boolean isOK = f.exists();
+
+will load f with its byte[] data, if the file exists. 
+
+But if after that, we use:
+
+File f2 = new File(f.getAbsolutePath());
+
+f2 will not contain that data. Such copying should be done as:
+
+File f2 = new File(f);
+
+in which case, the byte[] data will be transferred.
+
+
+SwingJS uses the following criteria to determine if File.exists() returns true:
+
+(1) if this File object has been used directly to read data, or 
+(2) if reading data using this File object is successful.
+
+Note that you cannot check to see if a file exists before input or if it 
+was actually written or if it already exists prior to writing in SwingJS.  
+
+Thus, you should check each use of file.exists() carefully, and if necessary, provide a J2sNative 
+block that gives an appropriate "OK" message, for example:
+
+(/** @j2sNative 1 ? false : */ outputfile.exits())
+
+or 
+
+(/** @j2sNative 1 ? true : */ inputfile.exits())
+
+Temporary files can be created in SwingJS. SwingJS will maintain a pseudo-filesystem for files 
+created with File.createTempFile(). This is useful in that closure of writing to a temporary file 
+does not generate a pseudo-download to the user's machine.
+
+
+UNIMPLEMENTED CLASSES BY DESIGN
+===============================
+
+The SwingJS implementation of the following classes are present 
+in a way that gracefully bypasses their functionality:
+
+accessibility
+security
+serialization
+
+
+
+TODO LIST FOR UNIMPLEMENTED CLASSES
+===================================
+
+JEditorPane (minimal implementation) - DONE 12/2018; some issues still
+JSplitPane - DONE 8/2018
+JTabbedPane - DONE 10/2018
+JTree - done 12/2019
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+Thread.currentThread() == dispatchThread
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+See below for a full discussion.
+
+Restrictions on long
+Restriction on BitSet and Scanner
+HashMap, Hashtable, and HashSet iterator ordering
+interning, new String("xxx") vs "xxx"
+Names with "$" and "_"
+positive integers do not add to give negative numbers
+ArrayIndexOutOfBounds
+java.awt.Color
+native methods
+javax.swing.JFileDialog
+key focus
+LookAndFeel and UI Classes
+System.exit(0) does not stop all processes
+list cell renderers must be JComponents
+myClass.getField not implemented
+"window" and other reserved JavaScript names
+reserved field and method names
+qualified field and method names
+missing Math methods
+Component.getGraphics(), Graphics.dispose()
+Graphics.setClip()
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+OS-dependent classes
+AWT component peers
+some aspects of reflection
+
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+threads
+modal dialogs
+image loading
+BigDecimal not fully implemented 
+no format internationalization
+no winding rules
+text-related field implementation
+Formatter/Regex limitations
+integer 1/0 == Infinity
+
+======================================================================== 
+
+DISCUSS
+=======
+
+Table row/col sorter needs checking after removal of java.text.Collator references
+
+I had to move all of SunHints class to RenderingHints, or the 
+two classes could not be loaded. Shouldn't be a problem, I think. The sun classes are
+not accessible to developers in Java anyway, since they are generally package private.
+
+========================================================================== 
+
+//////////////////////////////////////////////////////////////////////////////
+
+UNIMPLEMENTED CLASSES
+=====================
+
+accessibility
+-------------
+
+All Accessibility handling has been commented out to save the download footprint.
+This removes the need for sun.misc.SharedSecrets as well. 
+Nothing says we could not implement accessibility. We just didn't.
+
+
+security
+--------
+
+All JavaScript security is handled by the browser natively. 
+Thus, Java security checking is no longer necessary, and 
+java.security.AccessController has been simplified to work without
+native security checking.
+
+Note that private methods in a class are REALLY private. 
+
+
+serialization
+-------------
+
+All serialization has been removed. It was never very useful for Swing anyway, 
+because one needs exactly the same Java version to save and restore serialized objects.
+
+
+keyboard accelerators and mnemonics
+-----------------------------------
+
+This work was completed in the spring of 2019. Note that in a browser, some 
+key strokes, particularly CTRL-keys, are not available. Bummer.
+
+
+MINOR ISSUES--required some rewriting/refactoring by Bob and Udo  
+================================================================
+
+
+Thread.currentThread() == dispatchThread
+----------------------------------------
+
+changed to JSToolkit.isDispatchThread()
+
+
+MINOR ISSUES--requiring some rewriting/refactoring outside of SwingJS  
+=====================================================================
+
+restrictions on long
+--------------------
+
+Java's 64-bit long type is not supported in JavaScript. There is no Int64Array in JavaScript,
+and 0x20000000000000 + 1 evaluates to 0x20000000000000, not 0x20000000000001. 
+(Likewise, -0x20000000000000 - 1 is left unchanged.) 
+
+The largest "integer" value in JavaScript is 9007199254740991 (9.007199254740991E13, or 0x1FFFFFFFFFFFFFF).
+Effectively, you get to use only 53 bits of the long, not 64. Trying to set a long larger than
+0x1FFFFFFFFFFFFFF or smaller than -0x1FFFFFFFFFFFFFF will result in a NumberFormatException.
+
+The transpiler handles conversion to long the same as Java for all cases other than from double. 
+
+For small double values, there is no problem, and, in fact, this is a known trick used to round 
+doubles and floats toward zero:
+
+double d;
+d = (long) 3.8;
+assert(d == 3);
+d = (long) -3.8;
+assert(d == -3);
+
+SwingJS will evaluate (long) d as 0 for d > 9007199254740991 
+or d < -9007199254740991, same as Java returns for Double.NaN.
+So, in Java we have:
+
+               assert(((long) Double.NaN) == 0);
+               assert(((int) Double.NaN) == 0);
+               assert(((long) Float.NaN) == 0);
+               assert(((int) Float.NaN) == 0);
+
+and also, in JavaScript only, we also have:
+
+               double d = 0x2000000000000L;
+               assert(((long) d) == 0);
+
+
+restrictions on BitSet and Scanner
+----------------------------------
+
+Because of the issue of long being only 53 bits, any time a method returns a long value, considerations must
+be made as to whether this will work in JavaScript. In particular, BitSet and Scanner have issues. 
+
+In SwingJS, java.util.BitSet has been implemented as a 32-bit integer-based bitset. This was no problem in
+Java 6, but starting with Java 7, a method was added to BitSet that allows for the extraction of the 
+underlying long[] word data. This is not work in JavaScript. Instead, SwingJS java.util.Bitset.toLongArray() will deliver 
+32-bit int[] data.
+
+SwingJS Scanner has hasNextLong() and nextLong(), and although it will scan through long numbers,
+Scanner will choke on long numbers greater than the JavaScript 53-bit limit. hasNextLong() will 
+return false, and nextLong() will throw an InputMismatchException triggered by the NumberFormatException
+thrown by Long.parseLong(). 
+
+
+HashMap, Hashtable, and HashSet iterator ordering
+-------------------------------------------------
+
+In Java, iterators for HashMap, Hashtable, and HashSet do not guarantee any particular order. 
+From the HashMap documentation for Java 8:
+
+       This class makes no guarantees as to the order of the map; in particular, it does not 
+       guarantee that the order will remain constant over time.
+Likewise, for HashSet (because it is simply a convenience method for HashMap<Object,PRESENT>:
+
+       [HashSet] makes no guarantees as to the iteration order of the set.
+
+JavaScript's Map object is different. It is basically a LinkedHashMap, so it guarantees iteration
+in order of object addition.
+
+Starting with java2script 3.2.9.v1, these classes use the JavaScript Map object rather than hash codes
+whenever all keys are strictly of JavaScript typeof "string". If any key is introduced that is not a string, the
+implementation falls back to using hash codes, the same as Java. 
+
+Note strings created using new String("xxxx") are NOT typeof "string"; they are typeof "object".
+
+The result is significantly faster performance (3-12 x faster) than originally, and up to 3 x faster
+performance in JavaScript than in Java itself. Right. Faster than Java. 
+
+The JavaScript Map implementation is implemented UNLESS the constructor used is the one that
+specifies both initial capacity and load factor in their constructor. Thus, 
+
+new Hashtable()
+new HashMap()
+new HashMap(16)
+new HashSet()
+
+all use the JavaScript Map. But
+
+new Hashtable(11, 0.75f)
+new HashMap(16, 0.75f)
+new HashSet(16, 0.75f)
+
+do not. 
+
+This design allows for opting out of the JavaScript Map use in order to retain the exact behavior of 
+iterators in JavaScript as in Java.
+
+
+interning, new String("xxx") vs "xxx"
+-------------------------------------
+
+Note that the following are true in JavaScript:
+
+typeof new String("xxxx") == "object"
+typeof "xxxx" == "string"
+var s = "x";typeof ("xxx" + s) == "string"
+
+There is no equivalence to this behavior in Java, where a String is a String is a String.
+
+Be aware that SwingJS does not always create a JavaScript String object using JavaScript's 
+new String(...) constructor. It only does this for Java new String("xxxx") or new String(new String()). 
+
+In all other cases, new String(...) (in Java) results in a simple "xxxx" string in JavaScript. 
+That is, it will be JavaScript typeof "string", not typeof "object". 
+
+The reason for this design is that several classes in the Java core use toString() 
+methods that return new String(), and those classes that do that would cause a JavaScript error
+if implicitly stringified if new String() returned a JavaScript String object. 
+
+This is fine in JavaScript
+
+test1 = function() { return { toString:function(){ return "OK" } } }
+"testing" + new test1()
+>> "testingOK"
+
+But for whatever reason in JavaScript:
+
+test2 = function() { return { toString:function(){ return new String("OK") } } }
+"testing" + new test2()
+>> Uncaught TypeError: Cannot convert object to primitive value
+
+The lesson here is never to use 
+
+  return new String("...");
+
+in a Java toString() method. In Java it will be fine; in JavaScript it will also be fine as long as
+that method is never called in JavaScript implicitly in the context of string concatenation.
+
+A note about interning. Consider the following six Java constructions, where we have a == "x";
+
+"xxx"
+"xx" + "x"
+new String("xxx").intern()
+
+new String("xxx")
+"xx" + a.toString()
+"xx" + a
+
+All six of these will return java.lang.String for .getClass().getName().
+However, the first three are String literals, while the last three are String objects. 
+Thus:
+        "xxx" == "xxx"
+        "xxx" == "xx" + "x"
+        "xxx" == new String("xxx").intern()
+
+but none of the other three are equivalent to "xxx" or each other:
+
+              "xxx" != new String("xxx")
+              "xxx" != "xx" + a.toString()
+              "xxx" != "xx" + a
+  new String("xxx") != new String("xxx") 
+           "xx" + a != new String("xxx") 
+
+etc.
+
+As in Java, in SwingJS, all of the following Java assertions pass as true:
+
+               assert("xxx" == "xx" + "x"); 
+               assert("xxx" == ("xx" + a).intern()); 
+               assert("xxx" === new String("xxx").intern()); 
+               
+and both of these do as well:
+
+               assert(new String("xxx") != "xxx"); 
+               assert(new String("xxx") != new String("xxx")); 
+
+But the following two fail to assert true:
+
+        assert("xxx" != "xx" + a);
+        assert("xxx" != "xx" + a.toString());
+
+because in JavaScript, both of these right-side expressions evaluate to a simple "interned" string.
+
+In Java, however, these assertions are true because Java implicitly "boxes" String 
+concatentaion as a String object, not a literal. 
+
+Most of us know not to generally use == with Strings unless they are explicitly interned. 
+Where this problem may arise, though, is in IdentityHashMap, which compares objects using 
+System.identityHashCode(), which is not the same for different objects or their string literal equivalents.
+
+My recommendation, if you need to use IdentityHashMap with strings is to always use an explicit String.intern()
+for any keys -- unless you really want to keep every string as separate keys even if they are the same sequence, 
+in which case, use new String(). This will work in Java and in  JavaScript.
+
+Be aware when working with strings that come from SwingJS and are being used by other JavaScript modules
+that those that are String objects will return "object" for the JavaScript typeof operator, not "string".
+
+The easy way to ensure this is no problem is to concatenate strings with "" to force immediate interning:
+
+  var x = aJavaObject.getString() + "";
+
+unless you are certain that the string is being returned is a raw JavaScript string.   
+
+Names with "$" and "_"
+----------------------
+
+For the most part, this should be no problem. 
+
+Note that the use of $ and _ in Java field names has always been discouraged:
+[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html]
+
+       You may find some situations where auto-generated names will contain the dollar sign, 
+       but your variable names should always avoid using it. A similar convention 
+       exists for the underscore character; while it's technically legal to begin your 
+       variable's name with "_", this practice is discouraged.
+
+Some impacts of transpiling method names with full qualification:
+
+1) SwingJS will introduce fields that start with $ or _. These will not conflict
+   if the above convention is followed.
+   
+2) Fields that have the same Java name as a method are not an issue. 
+
+3) Fields that have a Java name with $ that matches a transpiled method name, 
+   such as toString$, will need to be refactored in Java to not have that name collision.
+   
+4) Fields in a subclass that have the same name as private fields in a superclass
+   represent a name collision, because the superclass method needs to call its private
+   field even if invoked from a subclass. The solution was to modify the subclass field
+   name using one or more prepended $.
+   
+5) Use of Class.getDeclaredMethods() reflection will return Method objects having the transpiled 
+   name, not the Java name. This could require some j2sNative adjustment 
+   to strip the $... parameters from the name if that is needed. 
+
+6) Use of Method.getParameterTypes() should work fine, provided class names
+   do not contain "_". This is because the transpiler converts "." to "_" when
+   creating the fully qualified JavaScript name.
+
+
+positive integers do not add to give negative numbers
+-----------------------------------------------------
+
+In Java, the following is true:
+
+  2000000000 + 2000000000 == -294967296
+
+But in SwingJS, that will be 4000000000. So, for example, the following
+strategy will fail in SwingJS:
+
+               int newLength = lineBuf.length * 2;
+               if (newLength < 0) {
+                       newLength = Integer.MAX_VALUE;
+               }
+
+"-1" in JavaScript is not 0xFFFFFFFF.
+
+And one must take care to not compare a negative number with a 32-bit mask. So
+
+(b & 0xFF000000) == 0xFF000000
+
+is true in Java for (int) b = -1, but is false in JavaScript, because 0xFF000000 is 4278190080, 
+while (-1 & 0xFF000000) is, strangely enough, -16777216, and, in fact, 
+
+(0xFF000000 & 0xFF000000) != 0xFF000000
+
+because -16777216 is not 4278190080.
+
+The fix is that one must compare similar operations:
+
+if ((b & 0xFF000000) == (0xFF000000 & 0xFF000000)) .....
+
+Importantly, the JavaScript Int32Array does behave properly. From 
+the Firefox developer console:
+
+>> x = new Int32Array(1)
+<- Int32Array(1) [ 0 ]
+>> x[0] = 4000000000
+<- 4000000000
+>> x[0]
+<- -294967296
+
+Notice that, perhaps unexpectedly, the following two constructs produce 
+different results in JavaScript:
+
+x = new Int32Array(1);
+b = x[0] = 4000000000;
+
+(b will be 4000000000)
+
+and
+
+x = new Int32Array(1);
+x[0] = 4000000000;
+b = x[0];
+
+(b will be -294967296)
+
+
+SwingJS leverages array typing to handle all byte and short arithmetic so as
+to ensure that any byte or short operation in JavaScript does give the same 
+result in Java. The design decision to not also do this with integer math was
+a trade-off between performance and handling edge cases.
+
+
+ArrayIndexOutOfBounds
+---------------------
+
+You cannot implicitly throw an ArrayIndexOutOfBoundsException in JavaScript.
+JavaScript will simply return "undefined", not throw an Exception. So:
+
+boolean notAGoodIdeaIsOutOfBounds(String[] sa, int i) {
+  try {
+     return (sa[i] == sa[i]);
+  } catch (ArrayIndexOutOfBoundsException e) {
+       return false;
+  }
+}
+
+will work in Java but not in JavaScript. Code should not depend upon this sort 
+of trap anyway, if you ask me. 
+
+Throwable vs Error vs Exception
+-------------------------------
+
+True JavaScript errors are trapped as Throwable, whereas you can still trap
+Error and Exception as well. So if you want to be sure to catch any JavaScript
+error, use try{}catch (Throwable t){}, not try{}catch (Exception e){}. 
+
+j
+ava.awt.Color
+--------------
+
+ColorSpace: only "support" CS_sRGB.
+
+ TODO -- any volunteers??
+
+javax.swing.JFileDialog
+-----------------------
+
+HTML5 cannot expose a file reading directory structure. But you certainly 
+can still do file reading and writing. It just works a little differently.
+It's a simple modification:
+
+               b = new JButton("FileOpenDialog");
+               b.addActionListener(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               JFileChooser fc = new JFileChooser();
+                               Test_Dialog.this.onDialogReturn(fc.showOpenDialog(Test_Dialog.this));
+                               // Java will wait until the dialog is closed, then enter the onDialogReturn method.
+                               // JavaScript will exit with NaN immediately, and then call back with its actual value
+                               // asynchronously.
+                       }
+
+               });
+       
+               public void onDialogReturn(int value) {
+                       if (value != Math.floor(value))
+                               return; // in JavaScript, this will be NaN, indicating the dialog has been opened
+                       // If we are here, the dialog has closed, in both Java and JavaScript.
+                       System.out.println("int value is " + value);
+               }
+
+
+       @Override
+       public void propertyChange(PropertyChangeEvent event) {
+               Object val = event.getNewValue();
+               String name = event.getPropertyName();
+               System.out.println(name);
+               switch (event.getSource().getClass().getName()) {
+               case "javax.swing.JOptionPane":
+                       switch (name) {
+                       case "inputValue":
+                               onDialogReturn(val);
+                               return;
+                       case "value":
+                               if (val instanceof Integer)
+                                       onDialogReturn(((Integer) val).intValue());
+                               else
+                                       onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.ColorChooserDialog":
+                       switch (name) {
+                       case "SelectedColor":
+                               onDialogReturn(val);
+                               return;
+                       }
+                       break;
+               case "javax.swing.JFileChooser":
+                       switch (name) {
+                       case "SelectedFile":
+                               File file = (File) val;
+                               byte[] array = (val == null ? null : /** @j2sNative file.秘bytes || */
+                                               null);
+                               onDialogReturn("fileName is '" + file.getName() + "'\n\n" + new String(array));
+                               return;
+                       }
+                       break;
+               }
+               System.out.println(
+                               event.getSource().getClass().getName() + " " + event.getPropertyName() + ": " + event.getNewValue());
+       }
+
+
+Developers are encouraged to create a separate class that handles general calls to JFileDialog. 
+An example class can be found in the SwingJS distribution as 
+
+/sources/net.sf.j2s.java.core/src/javajs/async/AsyncFileChooser.java.
+
+
+javax.swing.JOptionPane dialogs
+-------------------------------
+
+For this action to work, the parentComponent must implement
+propertyChangeListener, and any call to JOptionPanel should allow for
+an asynchronous response, meaning that there is no actionable code following the
+call to the dialog opening. 
+
+In addition, for compatibility with the Java version, implementation should
+wrap the call to getConfirmDialog or getOptionDialog in a method call to
+handle the Java:
+
+onDialogReturn(JOptionPane.showConfirmDialog(parentFrame,
+messageOrMessagePanel, "title", JOptionPane.OK_CANCEL_OPTION));
+
+Then parentFrame.propertyChange(event) should also call onDialogReturn.
+
+This will then work in both Java and JavaScript.
+
+Note that there is an int and an Object version of onDialogReturn().
+
+
+In JavaScript:
+
+The initial return from JOptionPane.showConfirmDialog and showMessageDialog
+will be (SwingJS) JDialog.ASYNCHRONOUS_INTEGER (NaN), testable as an impossible 
+Java int value using ret != -(-ret) if the parent implements PropertyChangeListener, or -1
+(CLOSE_OPTION) if not.
+
+For showOptionDialog (which returns Object) or showInputDialog (which returns
+String), the initial return will be (SwingJS) JDialog.ASYNCHRONOUS_OBJECT, testable as
+((Object) ret) instanceof javax.swing.plaf.UIResource if the parent implements
+PropertyChangeListeneer, or null if not.
+
+The second return will be the desired return.
+
+In Java:
+
+The initial return will be the one and only modal final return.
+
+
+
+For full compatibility, The calling method must not continue beyond this
+call.
+
+All of the standard Java events associated with Components are also
+available.
+
+Certain fall back mechanisms are possible, where onReturn does not exist, but
+only for the following cases:
+
+
+For showMessageDialog, for WARNING_MESSAGE and ERROR_MESSAGE, a simple
+JavaScript alert() is used, returning 0 (OK_OPTION) or -1 (CLOSED_OPTION).
+
+For showInputDialog, if the message is a string, a simple JavaScript prompt()
+with input box is used, returning the entered string or null.
+
+For showConfirmDialog, a simple JavaScript confirm() is used, in which case:
+
+for YES_NO_OPTION: YES_OPTION or NO_OPTION
+
+for YES_NO_CANCEL_OPTION: YES_OPTION or CANCEL_OPTION
+
+for OK_CANCEL_OPTION or any other: OK_OPTION or CANCEL_OPTION
+
+Note that you should implement a response for CLOSED_OPTION for
+showConfirmDialog. For other dialogs, a null return indicates the dialog was
+closed, just as for Java.
+
+Developers are encouraged to create a separate class that handles general calls. 
+An example class can be found in the SwingJS distribution as src/javajs/async/AsyncDialog.java.
+Very simple modifications to the Java allows asynchronous operation using AsyncDialog. Here
+is a simple "do you want to close this frame" example, where you can see that what we have
+done is to set the reply into an ActionListener that is defined in the constructor of 
+the AsyncDisplay object:
+
+// Original:
+//
+//     private void promptQuit() {
+//             int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+//             switch (sel) {
+//             case JOptionPane.YES_OPTION:
+//                     resultsTab.clean();
+//                     seqs.dispose();
+//                     if (fromMain) {
+//                             System.exit(0);
+//                     }
+//                     break;
+//             }
+//     }
+
+       private void promptQuitAsync() {
+               new AsyncDialog(new ActionListener() {
+
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                           int sel = ((AsyncDialog)e.getSource()).getOption();
+                               switch (sel) {
+                               case JOptionPane.YES_OPTION:
+                                       resultsTab.clean();
+                                       seqs.dispose();
+                                       if (fromMain) {
+                                               System.exit(0);
+                                       }
+                                       break;
+                               }
+                       }}).showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
+       }
+
+Very simple! 
+
+
+native methods
+--------------
+
+The J2S compiler ignores all static native method declarations.
+Anything of this nature needs to be implemented in JavaScript if it is needed,
+using j2sNative blocks:
+
+/**
+ * @j2sNative
+ *
+ *    var putYourJavaScriptCodeHere
+ *
+ */
+ Note that if you follow that directly with a {...} block, then 
+ the javadoc code will run in JavaScript, and the {...} code will run in Java.
+key Focus
+---------
+
+As of June, 2019, the keyboard focus manager is fully implemented. 
+The one catch is that JTextPane and JTextArea, which already consume
+VK_TAB in Java, cannot use CTRL-TAB to continue a tabbing cycle around
+the components in a window. Instead, CTRL-TAB is absorbed by the browser. 
+
+
+LookAndFeel and UI Classes
+--------------------------
+
+SwingJS implements the native browser look and feel as swingjs.plaf.HTML5LookAndFeel. 
+There are small differences between all look and feels -- MacOS, Windows, SwingJS.
+
+Expert developers know how to coerce changes in the UI by subclassing the UI for a 
+component. This probably will not work in SwingJS. 
+
+Note that LookAndFeel in Java usually determines canvas size in a Frame because 
+different operating systems (Mac OS vs Windows vs HTML5) will have 
+different edge sizes on their frames. If you want to ensure a component size, 
+use getContentPane().setPreferredSize().
+
+
+System.exit(0) does not stop all processes
+------------------------------------------
+
+Although System.ext(int) has been implemented in JavaScript, it just closes the 
+frames, stops all pending javax.swing.Timer objects in the queue, and runs any 
+threads added using Runtime.getRuntime().addShutdownHook(Thread).
+It may not stop all "threads." So don't rely on that.
+Applications are responsible for shutting down prior to executing System.exit(0). 
+
+
+myClass.getField not implemented
+--------------------------------
+
+java.lang.reflect.Field is implemented minimally. It is not
+certain that Field.getDeclaringClass() will work. If you just want a 
+value of a field, you can do this:
+
+/**
+ *@j2sNative
+ *
+ * return myClass[name]
+ */   
+
+But that is not a java.lang.reflection.Field object.
+
+
+"window" and other reserved JavaScript names
+--------------------------------------------
+
+No reserved top-level JavaScript name is allowed for a package name. So, for example, 
+one must rename packages such as "window" or "document" to names such as "win" or "doc".
+
+reserved field and method names
+-------------------------------
+
+In order to minimize the chance of added SwingJS field and method names colliding with ones 
+developers might use in subclassing Java classes, we have added U+79D8 (first character of Mandarin 
+"secret") to the characters already disrecommended by Java documentation ("$" and "_"). The only problem
+would be if you use that character followed by certain English words in certain classes. For example
+\u79D8canvas for JComponents (in java.awt.JSComponent) and \u79D8byte (in java.io.File).
+
+qualified field and method names
+--------------------------------
+
+Method names in SwingJS are fully qualified, meaning two methods with the same Java name but different
+parameters, such as write(int) and write(double), must not have the same name in JavaScript. (In this
+case, we will have write$I and write$D.) However, in certain cases it may be desirable to leave the
+method names unqualified. In particular, when an interface actually represents a JavaScript object, 
+the transpiler can leave a method name unqualified. The default situation for this is a class name 
+includes ".api.js" (case-sensitive). This means that any method in any class in a package js within 
+a package api, or any private interface js that has an outer interface api, will have all-unqualified
+methods. An example of this is swingjs.plaf.JSComboPopupList, which needs to communicate with a jQuery 
+object directly using the following interface:
+
+       private interface api {
+
+               interface js extends JQueryObject {
+
+                       abstract js j2sCB(Object options);
+
+                       abstract Object[] j2sCB(String method);
+
+                       abstract Object[] j2sCB(String method, Object o);
+
+                       abstract Object[] j2sCB(String method, int i);
+
+                       abstract int j2sCB(String OPTION, String name);
+
+               }
+       }
+
+Notice that all these variants of j2sCB() will call the same method in JavaScript by design.
+
+
+missing Math methods
+--------------------
+
+java.lang.Math is worked out, but some methods are missing, either because they
+involve long integer value that are inaccessible in JavaScript, or because I just
+didn't implement them. This is a result of continued Java development. 
+It is easy enough to add these methods if you have the source. They go into j2sClazz.js, 
+which is combined with other initial libraries into swingjs2.js by build_site.xml
+
+
+Component.getGraphics(), Graphics.dispose()
+-------------------------------------------
+
+Use of component.getGraphics() is discouraged in Java and in SwingJS. 
+Specifically in SwingJS, any call to component.getGraphics() or 
+BufferedImage.createGraphics() or Graphics.create(...) should be matched with graphics.dispose(), 
+particularly when it is called outside the context of a paint(Graphics)
+call from the system. 
+
+If you see your graphics scrolling down the page with each repaint, 
+look for where you have used Component.getGraphics() and not Graphics.dispose().
+For example, this will definitely NOT work in SwingJS:
+
+  this.paint(getGraphics())
+  
+and really should not work in Java, either, as it is technically a resource memory leak.
+
+Instead, if you really do not want to use repaint(), use this:
+
+  Graphics g = getGraphics();
+  paint(g);
+  g.dispose();
+
+
+
+Graphics.setClip()
+------------------
+
+The HTML5 canvas.clip() method is permanent. You can only reset the clip using
+save/restore. This is different from Java, where you can temporarily change it using
+
+  Shape oldClip = Graphics.getClip();
+  Graphics.setClip(newClip);
+   ...
+  Graphics.setClip(oldClip); 
+
+If you need to do something like this, you must schedule the paints
+to not have overlapping clip needs.
+
+
+MAJOR ISSUES--for Bob and Udo within SwingJS
+============================================
+
+fonts
+-----
+
+Fonts and FontMetrics will all be handled in JavaScript. Font matching will 
+not be exact, and composite (drawn) fonts will not be supported. 
+
+SwingJS handles calls such as font.getFontMetrics(g).stringWidth("xxx") by 
+creating a <div> containing that text, placing it in an obscure location on 
+the page, and reading div.getBoundingClientRect(). This is a VERY precise
+value, but can be a pixel or two off from what Java reports for the same font.
+OS-dependent classes
+--------------------
+
+Static classes such as:
+
+   java.awt.Toolkit
+   java.awt.GraphicsEnvironment
+   
+   
+which are created using Class.forName are implemented using classes in the swingjs package.
+
+AWTAccessor is not implemented. 
+
+   
+AWT component peers and component "ui" user interfaces
+------------------------------------------------------
+
+ComponentPeer is a class that represents a native AWT component.
+Components with such peers are called "heavy-weight" components.
+They are expected to do the dirty work of graphics drawing. 
+
+Java Swing implements peers only for JApplet, JDialog, JFrame, and JWindow. 
+References to such objects have been removed, but clearly there must be 
+some connection to similar DOM objects, even for "light-weight" components. 
+
+
+  
+MAJOR ISSUES--to be resolved by implementers
+============================================
+
+fonts
+-----
+
+Glyph/composite/outline fonts are not supported.
+   
+
+
+threads
+-------
+
+Thread locking and synchronization are not relevant to JavaScript.
+Thus, anything requiring "notify.." or "waitFor.." could be a serious issue.
+All threading must be "faked" in JavaScript. Specifically not available is:
+
+  Thread.sleep()
+  
+javax.swing.AbstractButton#doClick(pressTime) will not work, as it requires Thread.sleep();
+    
+However, java.lang.Thread itself is implemented and used extensively. 
+
+Methods thread.start() and thread.run() both work fine. 
+
+For simple applications that use Thread.sleep() just to have a delay, as in a frame rate, for 
+example, one can use javax.swing.Timer instead. That is fully implemented. 
+
+Likewise, java.util.Timer can be replaced with no loss of performance with javax.Swing.Timer.
+Note that java.util.TimerTask is implemented, but it can also be replaced by an implementation of Runnable.
+
+task = new TimerTask(){....};
+t = new java.util.Timer();
+t.schedule(task, 0, 1);
+
+becomes
+
+task = new TimerTask(){....}; // or task = new Runnable() {...}
+t = new javax.swing.Timer(1, new ActionListener() {
+       @Override
+       public void actionPerformed(ActionEvent e) {
+               task.run();
+       }
+};
+t.setInitialDelay(0); // not particularly necessary
+t.start();
+
+In addition, SwingJS provides swingjs.JSThread, which can be subclassed
+if desired. This class allows simple 
+
+  while(!interrupted()){
+       wait()
+       ...
+  }  
+
+action through an asynchronous function run1(mode). For example:
+
+       protected void run1(int mode) {
+               try {
+                       while (true)
+                               switch (mode) {
+                               case INIT:
+                                       // once-through stuff here
+                                       mode = LOOP;
+                                       break;
+                               case LOOP:
+                                       if (!doDispatch || isInterrupted()) {
+                                               mode = DONE;
+                                       } else {
+                                               Runnable r = new Runnable() {
+                                                       public void run() {
+                                                               // put the loop code here
+                                                       }
+                                               };
+                                               dispatchAndReturn(r);
+                                               if (isJS)
+                                                       return;
+                                       }
+                                       break;
+                               // add more cases as needed
+                               case DONE:
+                                       // finish up here
+                                       if (isInterrupted())
+                                               return;
+                                       // or here
+                                       break;
+                               }
+               } finally {
+                       // stuff here to be executed after each loop in JS or at the end in Java
+               }
+       }
+
+image loading
+-------------
+- All image loading in SwingJS is synchronous. A MediaTracker call will immediately return "complete".
+  However, it still may take one system clock tick to fully load images. Thus, it is recommended that
+  images be preloaded in the static block of the applet if it is necessary that they be available in init().
+  This is only an issue if you are trying to access the pixel buffer of the image in JavaScript. 
+  
+- Applet.getImage(path, name) will return null if the image does not exist. 
+
+- BufferedImage: only "support" imageType RGB and ARGB
+
+  -BH: This is a temporary edit, just to get us started. Certainly GRAY will be needed
+
+
+BigInteger and BigDecimal
+-------------------------
+
+java.math.BigInteger is fully supported; java.math.BigDecimal is roughed 
+in and not fully tested (07/2019). 
+
+Both classes present significant issues for JavaScript, as they are based in 
+Java's 64-bit long for all their operations. Here is the JavaDoc note I added
+to BigInteger:
+
+ * SwingJS note: Because of the limitations of JavaScript with regard
+ * to long-integer bit storage as a double, this implementation drops
+ * the integer storage bit length to 24, giving 48 for long and leaving
+ * the last 16 bits clear for the exponent of the double number. This should
+ * not affect performance significantly. It does increase the storage 
+ * size by about 33%. By bringing an "int" to 3 bytes, we can easily construct
+ * and use byte[] data intended for the original BitSet.  
+
+"Easily" may be a bit strong there. This was a serious challenge.
+
+BigDecimal seems to run normally, but in order to do that, my hack involves
+reducing the size of an integer that is allowed to be stored as such and not
+in byte[] as a BigInteger. I'm sure there is a performance hit, but it does work.
+
+no format internationalization
+------------------------------
+
+For now, just en for number and date formatters
+
+no winding rules
+----------------
+
+  When filling a graphic, only nonzero winding rule is implemented in HTML5 Canvas2D.
+
+
+
+text-related field implementation
+---------------------------------
+
+Text fields are:
+
+JTextField   (JavaScript <input type="text">)
+JTextArea    (JavaScript <textarea>)
+JTextPane    (JavaScript <div>)
+JEditorPane  (JavaScript <div>)
+
+For the initial implementation, we don't implement infinite undo/redo, and the abstract 
+document model is much less elaborate. Only PlainDocument (in the form of JSPlainDocument)
+is implemented. The Document returned by JTextField.getDocument() is a javax.swing.text.Document.
+
+All scrolling is handled by HTML5. javax.swing.AutoScroller is not implemented.
+public static methods .stop, .isRunning, .processMouseDragged require true Java threading
+and so are not implmented. javax.swing.text.View and its subclasses are not implemented. 
+
+The JS document model does not allow two text fields to address the same underlying document. 
+
+JavaScript is slightly different from Java in that the field value is changed asynchronously after
+the keypressed event, so Java actions that are keyed to KEY_PRESSED may not pick up the new 
+key value even after SwingUtilities.invokeLater() is called. Thus, key pressed actions may need
+to be recorded after a key released event instead. 
+
+Formatter/Regex limitations
+---------------------------
+
+Some browsers cannot process Regex "look-behind" process such as (?<=\W)
+java.util.regex.Matcher and Pattern use JavaScript's RegExp object rather than
+the native Java object. These are not identical. Only flags /igm are supported.
+Matcher.start(groupID) is not supported.
+
+java.util.Formatter will function correctly for all standard %... patterns.
+
+integer 1/0 == Infinity
+-----------------------
+
+1/0 in Java throws "java.lang.ArithmeticException: / by zero", but in JavaScript is just Infinity. 
+
+
+Summary
+-------
+
+These are all the known limitations of SwingJS. We have not found any of these limitations
+to be show-stoppers. The primary issue for newcomers to SwingJS is having the source code.
+You must check that source code for all your library jar files is available or, if you
+choose, you will need to decompile those classes. We have used decompilation on some projects,
+and it works just fine. So, technically, all we really need are JAR/class files. But the 
+source is by far superior. It's generally prettier, and it has the license information that
+may or may not be present with the JAR or class files. Use class files at your own risk.
+
+Bob Hanson
+2019.08.16
+
+
+Additional Issues
+-----------------
+
+Annotation is working for classes, methods, and fields (12/22/19). Method reflection, however,
+is limited. Interfaces do not expose their methods, as the transpiler does not actually transpile
+the interfaces themselves. And method reflection only includes annotated methods.
+
+java.util.concurrent is not fully elaborated. This package is rewritten to not actually use the
+memory handling capabilities of concurrency, which JavaScript does not have access to.
+
+System.getProperties() just returns a minimal set of properties.
+
+
diff --git a/swingjs/ver/pre-long/net.sf.j2s.core.jar b/swingjs/ver/pre-long/net.sf.j2s.core.jar
new file mode 100644 (file)
index 0000000..ce22a3e
Binary files /dev/null and b/swingjs/ver/pre-long/net.sf.j2s.core.jar differ
diff --git a/swingjs/ver/pre-long/timestamp b/swingjs/ver/pre-long/timestamp
new file mode 100644 (file)
index 0000000..5232ef4
--- /dev/null
@@ -0,0 +1 @@
+20201219150605 
index 9d3877c..d01b23e 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import java.util.Locale;
+
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Sequence;
@@ -91,7 +93,7 @@ public class AlignmentGenerator
       ps = new PrintStream(new File(args[6]));
     }
 
-    boolean nucleotide = args[0].toLowerCase().startsWith("n");
+    boolean nucleotide = args[0].toLowerCase(Locale.ROOT).startsWith("n");
     int width = Integer.parseInt(args[1]);
     int height = Integer.parseInt(args[2]);
     long randomSeed = Long.valueOf(args[3]);
index 1faf3f2..cc35976 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.analysis;
 
+import java.util.Locale;
+
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertNull;
@@ -182,7 +184,7 @@ public class RnaTest
         char second = bases.charAt(j);
         boolean result = Rna.isCanonicalOrWobblePair(first, second);
         String pair = new String(new char[] { first, second })
-                .toUpperCase();
+                .toUpperCase(Locale.ROOT);
         if (pair.equals("AT") || pair.equals("TA") || pair.equals("AU")
                 || pair.equals("UA") || pair.equals("GC")
                 || pair.equals("CG") || pair.equals("GT")
@@ -211,7 +213,7 @@ public class RnaTest
         char second = bases.charAt(j);
         boolean result = Rna.isCanonicalPair(first, second);
         String pair = new String(new char[] { first, second })
-                .toUpperCase();
+                .toUpperCase(Locale.ROOT);
         if (pair.equals("AT") || pair.equals("TA") || pair.equals("AU")
                 || pair.equals("UA") || pair.equals("GC")
                 || pair.equals("CG"))
index 348d871..91f844e 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.commands;
 
+import java.util.Locale;
+
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
@@ -286,7 +288,7 @@ public class EditCommandTest
     assertEquals("abcdefghjk", seqs[0].getSequenceAsString());
     assertEquals("fghjZ-xYopq", seqs[1].getSequenceAsString());
     // Dataset Sequence should always be uppercase
-    assertEquals("fghjZxYopq".toUpperCase(),
+    assertEquals("fghjZxYopq".toUpperCase(Locale.ROOT),
             seqs[1].getDatasetSequence().getSequenceAsString());
     assertEquals("qrstuvwxyz", seqs[2].getSequenceAsString());
     assertEquals("1234567890", seqs[3].getSequenceAsString());
@@ -317,7 +319,7 @@ public class EditCommandTest
     assertEquals(1, seq.getStart());
     assertEquals(8, seq.getEnd());
     // Dataset sequence always uppercase
-    assertEquals("ABxyZDEF".toUpperCase(),
+    assertEquals("ABxyZDEF".toUpperCase(Locale.ROOT),
             seq.getDatasetSequence().getSequenceAsString());
     assertEquals(8, seq.getDatasetSequence().getEnd());
 
@@ -343,7 +345,7 @@ public class EditCommandTest
     assertEquals(1, seq.getStart());
     assertEquals(8, seq.getEnd());
     // dataset sequence should be Uppercase
-    assertEquals("ABxyZDEF".toUpperCase(),
+    assertEquals("ABxyZDEF".toUpperCase(Locale.ROOT),
             seq.getDatasetSequence().getSequenceAsString());
     assertEquals(8, seq.getDatasetSequence().getEnd());
 
@@ -367,7 +369,7 @@ public class EditCommandTest
     // and ds is preserved
     assertTrue(dsseq == seqs[1].getDatasetSequence());
     // and it is unchanged and UPPERCASE !
-    assertEquals("fghjklmnopq".toUpperCase(), dsseq.getSequenceAsString());
+    assertEquals("fghjklmnopq".toUpperCase(Locale.ROOT), dsseq.getSequenceAsString());
     // and that alignment sequence start has been adjusted
     assertEquals(5, seqs[1].getStart());
     assertEquals(11, seqs[1].getEnd());
@@ -394,7 +396,7 @@ public class EditCommandTest
     // and ds is preserved
     assertTrue(dsseq == seqs[1].getDatasetSequence());
     // and it is unchanged AND UPPERCASE !
-    assertEquals("fghjklmnopq".toUpperCase(), dsseq.getSequenceAsString());
+    assertEquals("fghjklmnopq".toUpperCase(Locale.ROOT), dsseq.getSequenceAsString());
     // and that alignment sequence start has been adjusted
     assertEquals(5, seqs[1].getStart());
     assertEquals(11, seqs[1].getEnd());
index c8f998b..e95a8b5 100644 (file)
@@ -134,6 +134,21 @@ public class DBRefEntryTest
     assertEquals("3", ref1.getVersion());
 
     /*
+     * canonical == false superseded by canonical == true
+     */
+    ref1.setCanonical(false);
+    ref2.setCanonical(true);
+    assertTrue(ref1.updateFrom(ref2));
+    assertTrue(ref1.isCanonical());
+
+    /*
+     * canonical == true NOT superseded by canonical == false
+     */
+    ref1.setCanonical(true);
+    ref2.setCanonical(false);
+    assertFalse(ref1.updateFrom(ref2));
+
+    /*
      * version "source:n" with n>0 is not superseded
      */
     ref1.setVersion("UNIPROT:1");
index cf4294e..198cde3 100644 (file)
@@ -208,7 +208,7 @@ public class PDBEntryTest
     assertTrue(pdb1.updateFrom(pdb2));
     assertEquals(pdb1.getFile(), "filePath");
     assertEquals(pdb1.getType(), Type.FILE.toString());
-
+    assertEquals(pdb1.getChainCode(),"B");
     /*
      * change of file is not allowed
      */
index 4eb6dbf..a6e7429 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.datamodel;
 
+import java.util.Locale;
+
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
@@ -92,6 +94,18 @@ public class ResidueCountTest
     assertEquals(rc.getCount('.'), 4);
     assertFalse(rc.isUsingOtherData());
     assertFalse(rc.isCountingInts());
+    
+    rc.set(ResidueCount.GAP_COUNT, Short.MAX_VALUE-2);
+    assertEquals(rc.getGapCount(), Short.MAX_VALUE-2);
+    assertFalse(rc.isCountingInts());
+    rc.addGap();
+    assertEquals(rc.getGapCount(), Short.MAX_VALUE-1);
+    assertFalse(rc.isCountingInts());
+    rc.addGap();
+    assertEquals(rc.getGapCount(), Short.MAX_VALUE);
+    rc.addGap();
+    assertTrue(rc.isCountingInts());
+    assertEquals(rc.getGapCount(), Short.MAX_VALUE+1);
   }
 
   @Test(groups = "Functional")
@@ -169,7 +183,7 @@ public class ResidueCountTest
     ResidueCount rc = new ResidueCount(false);
     // expected characters (upper or lower case):
     String aas = "ACDEFGHIKLMNPQRSTVWXY";
-    String lower = aas.toLowerCase();
+    String lower = aas.toLowerCase(Locale.ROOT);
     for (int i = 0; i < aas.length(); i++)
     {
       rc.put(aas.charAt(i), i);
@@ -194,7 +208,7 @@ public class ResidueCountTest
     ResidueCount rc = new ResidueCount(true);
     // expected characters (upper or lower case):
     String nucs = "ACGTUN";
-    String lower = nucs.toLowerCase();
+    String lower = nucs.toLowerCase(Locale.ROOT);
     for (int i = 0; i < nucs.length(); i++)
     {
       rc.put(nucs.charAt(i), i);
index 129d7b3..2b44261 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.datamodel;
 
+import java.util.Locale;
+
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertNotNull;
@@ -110,6 +112,23 @@ public class SequenceTest
     assertTrue(sq.isProtein());
   }
 
+  @Test(groups = ("Functional"))
+  public void testIsProteinWithXorNAmbiguityCodes()
+  {
+    // test Protein with N - poly asparagine 
+    assertTrue(new Sequence("prot", "ASDFASDFASDFNNNNNNNNN").isProtein());
+    assertTrue(new Sequence("prot", "NNNNNNNNNNNNNNNNNNNNN").isProtein());
+    // test Protein with X
+    assertTrue(new Sequence("prot", "ASDFASDFASDFXXXXXXXXX").isProtein());
+    // test DNA with X
+    assertFalse(new Sequence("prot", "ACGTACGTACGTXXXXXXXX").isProtein());
+    // test DNA with N
+    assertFalse(new Sequence("prot", "ACGTACGTACGTNNNNNNNN").isProtein());
+    // test RNA with X
+    assertFalse(new Sequence("prot", "ACGUACGUACGUXXXXXXXXX").isProtein());
+    assertFalse(new Sequence("prot", "ACGUACGUACGUNNNNNNNNN").isProtein());
+  }
+
   @Test(groups = { "Functional" })
   public void testGetAnnotation()
   {
@@ -175,6 +194,35 @@ public class SequenceTest
     assertTrue(seq.getAlignmentAnnotations(null, null).isEmpty());
   }
 
+
+  @Test(groups = { "Functional" })
+  public void testGetAlignmentAnnotations_forCalcIdLabelAndDescription()
+  {
+    addAnnotation("label1", "desc1", "calcId1", 1f);
+    AlignmentAnnotation ann2 = addAnnotation("label2", "desc2", "calcId2",
+            1f);
+    addAnnotation("label2", "desc3", "calcId3", 1f);
+    AlignmentAnnotation ann4 = addAnnotation("label2", "desc3", "calcId2",
+            1f);
+    addAnnotation("label5", "desc3", null, 1f);
+    addAnnotation(null, "desc3", "calcId3", 1f);
+
+    List<AlignmentAnnotation> anns = seq.getAlignmentAnnotations("calcId2",
+            "label2", "desc3");
+    assertEquals(1, anns.size());
+    assertSame(ann4, anns.get(0));
+    /**
+     * null matching should fail
+     */
+    assertTrue(seq.getAlignmentAnnotations("calcId3", "label2",null).isEmpty());
+    
+    assertTrue(seq.getAlignmentAnnotations("calcId2", "label3",null).isEmpty());
+    assertTrue(seq.getAlignmentAnnotations("calcId3", "label5",null).isEmpty());
+    assertTrue(seq.getAlignmentAnnotations("calcId2", null,null).isEmpty());
+    assertTrue(seq.getAlignmentAnnotations(null, "label3",null).isEmpty());
+    assertTrue(seq.getAlignmentAnnotations(null, null,null).isEmpty());
+  }
+
   /**
    * Tests for addAlignmentAnnotation. Note this method has the side-effect of
    * setting the sequenceRef on the annotation. Adding the same annotation twice
@@ -766,7 +814,7 @@ public class SequenceTest
     } catch (IllegalArgumentException e)
     {
       // TODO Jalview error/exception class for raising implementation errors
-      assertTrue(e.getMessage().toLowerCase()
+      assertTrue(e.getMessage().toLowerCase(Locale.ROOT)
               .contains("implementation error"));
     }
     assertTrue(sq.getSequenceFeatures().isEmpty());
@@ -1414,6 +1462,21 @@ public class SequenceTest
     seq.addPDBId(pdbe5);
     assertEquals(4, seq.getAllPDBEntries().size());
     assertSame(pdbe5, seq.getAllPDBEntries().get(3));
+    
+    // add with a fake pdbid 
+    // (models don't have an embedded ID)
+    String realId = "RealIDQ";
+    PDBEntry pdbe6 = new PDBEntry(realId,null,Type.PDB,"real/localpath");
+    PDBEntry pdbe7 = new PDBEntry("RealID/real/localpath","C",Type.MMCIF,"real/localpath");
+    pdbe7.setFakedPDBId(true);
+    seq.addPDBId(pdbe6);
+    assertEquals(5,seq.getAllPDBEntries().size());
+    seq.addPDBId(pdbe7);
+    assertEquals(5,seq.getAllPDBEntries().size());
+    assertFalse(pdbe6.fakedPDBId());
+    assertSame(pdbe6,seq.getAllPDBEntries().get(4));
+    assertEquals("C",pdbe6.getChainCode());
+    assertEquals(realId, pdbe6.getId());
   }
 
   @Test(
@@ -2175,4 +2238,25 @@ public class SequenceTest
     assertEquals(0, seq.firstResidueOutsideIterator(cs.iterator()));
 
   }
+  @Test(groups= {"Functional"})
+  public void testTransferAnnotation() {
+    Sequence origSeq = new Sequence("MYSEQ","THISISASEQ");
+    Sequence toSeq = new Sequence("MYSEQ","THISISASEQ");
+    origSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q12345", null, true));
+    toSeq.transferAnnotation(origSeq, null);
+    assertTrue(toSeq.getDBRefs().size()==1);
+    
+    assertTrue(toSeq.getDBRefs().get(0).isCanonical());
+    
+    // check for promotion of non-canonical 
+    // to canonical (e.g. fetch-db-refs on a jalview project pre 2.11.2)
+    toSeq.setDBRefs(null);
+    toSeq.addDBRef(new DBRefEntry("UNIPROT", "0", "Q12345", null, false));
+    toSeq.transferAnnotation(origSeq, null);
+    assertTrue(toSeq.getDBRefs().size()==1);
+    
+    assertTrue("Promotion of non-canonical DBRefEntry failed",toSeq.getDBRefs().get(0).isCanonical());
+    
+    
+  }
 }
index 9e9d9a4..c927f04 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ext.ensembl;
 
+import java.util.Locale;
+
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertNull;
@@ -230,7 +232,7 @@ public class EnsemblCdnaTest
     assertTrue(testee.retainFeature(sf, accId));
 
     // test is not case-sensitive
-    assertTrue(testee.retainFeature(sf, accId.toLowerCase()));
+    assertTrue(testee.retainFeature(sf, accId.toLowerCase(Locale.ROOT)));
 
     // feature with wrong parent is not retained
     sf.setValue("Parent", "XYZ");
index 8b1e840..e16197a 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ext.ensembl;
 
+import java.util.Locale;
+
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
@@ -161,7 +163,7 @@ public class EnsemblGeneTest
     SequenceFeature sf3 = new SequenceFeature("NMD_transcript_variant", "",
             22000, 22500, 0f, null);
     // id matching should not be case-sensitive
-    sf3.setValue("Parent", geneId.toLowerCase());
+    sf3.setValue("Parent", geneId.toLowerCase(Locale.ROOT));
     sf3.setValue("id", "transcript3");
     genomic.addSequenceFeature(sf3);
 
@@ -271,18 +273,24 @@ public class EnsemblGeneTest
 
   /**
    * Check behaviour of feature colour scheme for EnsemblGene sequences.
-   * Currently coded to display exon and sequence_variant (or sub-types) only,
-   * with sequence_variant in red above exon coloured by label.
+   * Currently coded to hide all except exon and sequence_variant (or sub-types)
+   * only, with sequence_variant in red above exon coloured by label.
    */
   @Test(groups = "Functional")
   public void testGetFeatureColourScheme()
   {
     FeatureSettingsModelI fc = new EnsemblGene().getFeatureColourScheme();
-    assertTrue(fc.isFeatureDisplayed("exon"));
-    assertTrue(fc.isFeatureDisplayed("coding_exon")); // subtype of exon
-    assertTrue(fc.isFeatureDisplayed("sequence_variant"));
-    assertTrue(fc.isFeatureDisplayed("feature_variant")); // subtype
-    assertFalse(fc.isFeatureDisplayed("transcript"));
+    assertFalse(fc.isFeatureDisplayed("exon"));
+    assertFalse(fc.isFeatureHidden("exon"));
+    assertFalse(fc.isFeatureDisplayed("coding_exon")); // subtype of exon
+    assertFalse(fc.isFeatureHidden("coding_exon")); // subtype of exon
+    assertFalse(fc.isFeatureDisplayed("sequence_variant"));
+    assertFalse(fc.isFeatureHidden("sequence_variant"));
+    assertFalse(fc.isFeatureDisplayed("feature_variant")); // subtype
+    assertFalse(fc.isFeatureHidden("feature_variant")); // subtype
+    assertTrue(fc.isFeatureHidden("transcript"));
+    assertTrue(fc.isFeatureHidden("CDS"));
+
     assertEquals(Color.RED, fc.getFeatureColour("sequence_variant")
             .getColour());
     assertEquals(Color.RED, fc.getFeatureColour("feature_variant")
index 2832135..10882d3 100644 (file)
@@ -277,6 +277,10 @@ public class JmolParserTest
     assertEquals(structureData.getId(), "localstruct");
     assertNotNull(structureData.getSeqs());
     /*
+     * local structures have a fake ID
+     */
+    assertTrue(structureData.getSeqs().get(0).getAllPDBEntries().get(0).fakedPDBId());
+    /*
      * the ID is also the group for features derived from structure data 
      */
     String featureGroup = structureData.getSeqs().get(0)
index 7d0916b..80fffff 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ext.paradise;
 
+import java.util.Locale;
+
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.datamodel.AlignmentI;
@@ -151,11 +153,11 @@ public class TestAnnotate3D
         {
           {
             SequenceI struseq = null;
-            String sq_ = sq.getSequenceAsString().toLowerCase();
+            String sq_ = sq.getSequenceAsString().toLowerCase(Locale.ROOT);
             for (SequenceI _struseq : pdbf.getSeqsAsArray())
             {
               final String lowerCase = _struseq.getSequenceAsString()
-                      .toLowerCase();
+                      .toLowerCase(Locale.ROOT);
               if (lowerCase.equals(sq_))
               {
                 struseq = _struseq;
index f677cab..439401a 100644 (file)
@@ -342,7 +342,7 @@ public class ChimeraXCommandsTest
   {
     List<StructureCommandI> cmds = testee.startNotifications("to here");
     assertEquals(cmds.size(), 2);
-    assertEquals(cmds.get(0), new StructureCommand("info notify start models prefix ModelChanged jalview url to here"));
+    assertEquals(cmds.get(0), new StructureCommand("info notify start models jalview prefix ModelChanged url to here"));
     assertEquals(cmds.get(1), new StructureCommand("info notify start selection jalview prefix SelectionChanged url to here"));
   }
 
index 224a712..0b4dd92 100644 (file)
@@ -69,7 +69,7 @@ public class PDBFTSPanelTest
     String expectedString = "1xyz OR text:2xyz OR text:3xyz";
     String outcome = PDBFTSPanel.decodeSearchTerm("1xyz:A;2xyz;3xyz",
             "text");
-    // System.out.println("1 >>>>>>>>>>> " + outcome);
+    System.out.println("1 >>>>>>>>>>> " + outcome);
     assertEquals(expectedString, outcome);
 
     expectedString = "1xyz";
@@ -124,5 +124,13 @@ public class PDBFTSPanelTest
     assertTrue(!mainFrame.getTitle().equalsIgnoreCase(
             "PDB Sequence Fetcher"));
   }
+  
+  @Test
+  public void getFTSframeTitleTest() {
+    PDBFTSPanel searchPanel = new PDBFTSPanel(null);
+    String outcome = searchPanel.getFTSFrameTitle();
+    //System.out.println("FTS Frame title :" + outcome);
+    assertEquals(outcome, "PDB Sequence Fetcher");
+  }
 
 }
index bbd45aa..c7e35ed 100644 (file)
@@ -24,6 +24,7 @@ import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertTrue;
 
 import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.core.FTSRestClient;
 import jalview.fts.core.FTSRestRequest;
 import jalview.fts.core.FTSRestResponse;
 import jalview.gui.JvOptionPane;
@@ -93,6 +94,8 @@ public class PDBFTSRestClientTest
     {
       e1.printStackTrace();
     }
+    System.out.println("wantedFields >>" + wantedFields);
+
 
     FTSRestRequest request = new FTSRestRequest();
     request.setAllowEmptySeq(false);
@@ -196,6 +199,7 @@ public class PDBFTSRestClientTest
   @Test(groups = { "External" }, expectedExceptions = Exception.class)
   public void testForExpectedRuntimeException() throws Exception
   {
+    // JBPNote: looks like this test fails for no good reason - what exception was supposed to be raised ?
     List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
     wantedFields.add(PDBFTSRestClient.getInstance()
             .getDataColumnByNameOrCode("pdb_id"));
@@ -208,6 +212,7 @@ public class PDBFTSRestClientTest
   }
 
   // JBP: Is this actually external ? Looks like it is mocked
+  // JBP looks like the mock is not up to date for this test
   @Test(groups = { "External" })
   public void parsePDBJsonResponseTest()
   {
@@ -246,6 +251,7 @@ public class PDBFTSRestClientTest
     assertTrue(response.getSearchSummary() != null);
     assertTrue(response.getNumberOfItemsFound() == 931);
     assertTrue(response.getSearchSummary().size() == 14);
+    System.out.println("Search summary : " + response.getSearchSummary());
   }
 
   @Test(groups = { "Functional" })
@@ -351,7 +357,13 @@ public class PDBFTSRestClientTest
     }
   }
 
-  public String readJsonStringFromFile(String filePath) throws IOException
+  /**
+   * reads any string from filePath
+   * @param filePath
+   * @return
+   * @throws IOException
+   */
+  public static String readJsonStringFromFile(String filePath) throws IOException
   {
     String fileContent;
     BufferedReader br = new BufferedReader(new FileReader(filePath));
@@ -374,4 +386,346 @@ public class PDBFTSRestClientTest
     return fileContent;
   }
 
+  public static void setMock()
+  {
+    String[][] mocks = new String[2][];
+    mocks[0] = new String[] {
+        "https://www.ebi.ac.uk/pdbe/search/pdb/select?wt=json&fl=pdb_id,title,experimental_method,resolution&rows=500&start=0&q=(4igk+OR+1t15+OR+4ifi+OR+1t29+OR+3pxb+OR+4y2g+OR+1y98+OR+1jnx+OR+3pxa+OR+3k0h+OR+3k0k+OR+1n5o+OR+3pxc+OR+3pxd+OR+1t2u+OR+3k15+OR+3pxe+OR+3k16+OR+4ofb+OR+3coj+OR+7lyb+OR+1t2v+OR+4y18+OR+4jlu+OR+4u4a+OR+2ing+OR+7jzv+OR+6g2i+OR+1jm7+OR+1oqa)+AND+molecule_sequence:%5B''+TO+*%5D+AND+status:REL&sort=",
+        "{\n" + "  \"responseHeader\":{\n" + "    \"status\":0,\n"
+                + "    \"QTime\":0,\n" + "    \"params\":{\n"
+                + "      \"q\":\"(4igk OR 1t15 OR 4ifi OR 1t29 OR 3pxb OR 4y2g OR 1y98 OR 1jnx OR 3pxa OR 3k0h OR 3k0k OR 1n5o OR 3pxc OR 3pxd OR 1t2u OR 3k15 OR 3pxe OR 3k16 OR 4ofb OR 3coj OR 7lyb OR 1t2v OR 4y18 OR 4jlu OR 4u4a OR 2ing OR 7jzv OR 6g2i OR 1jm7 OR 1oqa) AND molecule_sequence:['' TO *] AND status:REL\",\n"
+                + "      \"fl\":\"pdb_id,title,experimental_method,resolution\",\n"
+                + "      \"start\":\"0\",\n" + "      \"sort\":\"\",\n"
+                + "      \"rows\":\"500\",\n" + "      \"wt\":\"json\"}},\n"
+                + "  \"response\":{\"numFound\":64,\"start\":0,\"docs\":[\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4ofb\",\n"
+                + "        \"resolution\":3.05,\n"
+                + "        \"title\":\"Crystal structure of human BRCA1 BRCT in complex with nonphosphopeptide inhibitor\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3pxe\",\n"
+                + "        \"resolution\":2.85,\n"
+                + "        \"title\":\"Impact of BRCA1 BRCT domain missense substitutions on phospho-peptide recognition: E1836K\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4jlu\",\n"
+                + "        \"resolution\":3.5,\n"
+                + "        \"title\":\"Crystal structure of BRCA1 BRCT with doubly phosphorylated Abraxas\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4y2g\",\n"
+                + "        \"resolution\":2.5,\n"
+                + "        \"title\":\"Structure of BRCA1 BRCT domains in complex with Abraxas single phosphorylated peptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Solution NMR\"],\n"
+                + "        \"pdb_id\":\"1oqa\",\n"
+                + "        \"title\":\"Solution structure of the BRCT-c domain from human BRCA1\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4u4a\",\n"
+                + "        \"resolution\":3.51,\n"
+                + "        \"title\":\"Complex Structure of BRCA1 BRCT with singly phospho Abraxas\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1t2v\",\n"
+                + "        \"resolution\":3.3,\n"
+                + "        \"title\":\"Structural basis of phospho-peptide recognition by the BRCT domain of BRCA1, structure with phosphopeptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3k15\",\n"
+                + "        \"resolution\":2.8,\n"
+                + "        \"title\":\"Crystal Structure of BRCA1 BRCT D1840T in complex with a minimal recognition tetrapeptide with an amidated C-terminus\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1t15\",\n"
+                + "        \"resolution\":1.85,\n"
+                + "        \"title\":\"Crystal Structure of the Brca1 BRCT Domains in Complex with the Phosphorylated Interacting Region from Bach1 Helicase\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3k16\",\n"
+                + "        \"resolution\":3.0,\n"
+                + "        \"title\":\"Crystal Structure of BRCA1 BRCT D1840T in complex with a minimal recognition tetrapeptide with a free carboxy C-terminus\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1t29\",\n"
+                + "        \"resolution\":2.3,\n"
+                + "        \"title\":\"Crystal structure of the BRCA1 BRCT repeats bound to a phosphorylated BACH1 peptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1y98\",\n"
+                + "        \"resolution\":2.5,\n"
+                + "        \"title\":\"Structure of the BRCT repeats of BRCA1 bound to a CtIP phosphopeptide.\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4ifi\",\n"
+                + "        \"resolution\":2.2,\n"
+                + "        \"title\":\"Structure of human BRCA1 BRCT in complex with BAAT peptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3k0k\",\n"
+                + "        \"resolution\":2.7,\n"
+                + "        \"title\":\"Crystal Structure of BRCA1 BRCT in complex with a minimal recognition tetrapeptide with a free carboxy C-terminus.\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3k0h\",\n"
+                + "        \"resolution\":2.7,\n"
+                + "        \"title\":\"The crystal structure of BRCA1 BRCT in complex with a minimal recognition tetrapeptide with an amidated C-terminus\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3pxd\",\n"
+                + "        \"resolution\":2.8,\n"
+                + "        \"title\":\"Impact of BRCA1 BRCT domain missense substitutions on phospho-peptide recognition: R1835P\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3pxc\",\n"
+                + "        \"resolution\":2.8,\n"
+                + "        \"title\":\"Impact of BRCA1 BRCT domain missense substitutions on phospho-peptide recognition: R1699Q\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3pxa\",\n"
+                + "        \"resolution\":2.55,\n"
+                + "        \"title\":\"Impact of BRCA1 BRCT domain missense substitutions on phospho-peptide recognition: G1656D\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1jnx\",\n"
+                + "        \"resolution\":2.5,\n"
+                + "        \"title\":\"Crystal structure of the BRCT repeat region from the breast cancer associated protein, BRCA1\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4igk\",\n"
+                + "        \"resolution\":1.75,\n"
+                + "        \"title\":\"Structure of human BRCA1 BRCT in complex with ATRIP peptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Solution NMR\"],\n"
+                + "        \"pdb_id\":\"1jm7\",\n"
+                + "        \"title\":\"Solution structure of the BRCA1/BARD1 RING-domain heterodimer\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4jlu\",\n"
+                + "        \"resolution\":3.5,\n"
+                + "        \"title\":\"Crystal structure of BRCA1 BRCT with doubly phosphorylated Abraxas\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"6g2i\",\n"
+                + "        \"resolution\":5.9,\n"
+                + "        \"title\":\"Filament of acetyl-CoA carboxylase and BRCT domains of BRCA1 (ACC-BRCT) at 5.9 A resolution\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3coj\",\n"
+                + "        \"resolution\":3.21,\n"
+                + "        \"title\":\"Crystal Structure of the BRCT Domains of Human BRCA1 in Complex with a Phosphorylated Peptide from Human Acetyl-CoA Carboxylase 1\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3pxb\",\n"
+                + "        \"resolution\":2.5,\n"
+                + "        \"title\":\"Impact of BRCA1 BRCT domain missense substitutions on phospho-peptide recognition: T1700A\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1t2u\",\n"
+                + "        \"resolution\":2.8,\n"
+                + "        \"title\":\"Structural basis of phosphopeptide recognition by the BRCT domain of BRCA1: structure of BRCA1 missense variant V1809F\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1n5o\",\n"
+                + "        \"resolution\":2.8,\n"
+                + "        \"title\":\"Structural consequences of a cancer-causing BRCA1-BRCT missense mutation\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4u4a\",\n"
+                + "        \"resolution\":3.51,\n"
+                + "        \"title\":\"Complex Structure of BRCA1 BRCT with singly phospho Abraxas\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4y2g\",\n"
+                + "        \"resolution\":2.5,\n"
+                + "        \"title\":\"Structure of BRCA1 BRCT domains in complex with Abraxas single phosphorylated peptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3pxe\",\n"
+                + "        \"resolution\":2.85,\n"
+                + "        \"title\":\"Impact of BRCA1 BRCT domain missense substitutions on phospho-peptide recognition: E1836K\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4ofb\",\n"
+                + "        \"resolution\":3.05,\n"
+                + "        \"title\":\"Crystal structure of human BRCA1 BRCT in complex with nonphosphopeptide inhibitor\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4y18\",\n"
+                + "        \"resolution\":3.5,\n"
+                + "        \"title\":\"Structure of BRCA1 BRCT domains in complex with Abraxas double phosphorylated peptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"2ing\",\n"
+                + "        \"resolution\":3.6,\n"
+                + "        \"title\":\"X-ray Structure of the BRCA1 BRCT mutant M1775K\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1t29\",\n"
+                + "        \"resolution\":2.3,\n"
+                + "        \"title\":\"Crystal structure of the BRCA1 BRCT repeats bound to a phosphorylated BACH1 peptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1t2v\",\n"
+                + "        \"resolution\":3.3,\n"
+                + "        \"title\":\"Structural basis of phospho-peptide recognition by the BRCT domain of BRCA1, structure with phosphopeptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1t15\",\n"
+                + "        \"resolution\":1.85,\n"
+                + "        \"title\":\"Crystal Structure of the Brca1 BRCT Domains in Complex with the Phosphorylated Interacting Region from Bach1 Helicase\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Solution NMR\"],\n"
+                + "        \"pdb_id\":\"1jm7\",\n"
+                + "        \"title\":\"Solution structure of the BRCA1/BARD1 RING-domain heterodimer\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4ifi\",\n"
+                + "        \"resolution\":2.2,\n"
+                + "        \"title\":\"Structure of human BRCA1 BRCT in complex with BAAT peptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4igk\",\n"
+                + "        \"resolution\":1.75,\n"
+                + "        \"title\":\"Structure of human BRCA1 BRCT in complex with ATRIP peptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"1y98\",\n"
+                + "        \"resolution\":2.5,\n"
+                + "        \"title\":\"Structure of the BRCT repeats of BRCA1 bound to a CtIP phosphopeptide.\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3k15\",\n"
+                + "        \"resolution\":2.8,\n"
+                + "        \"title\":\"Crystal Structure of BRCA1 BRCT D1840T in complex with a minimal recognition tetrapeptide with an amidated C-terminus\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3k0k\",\n"
+                + "        \"resolution\":2.7,\n"
+                + "        \"title\":\"Crystal Structure of BRCA1 BRCT in complex with a minimal recognition tetrapeptide with a free carboxy C-terminus.\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3k16\",\n"
+                + "        \"resolution\":3.0,\n"
+                + "        \"title\":\"Crystal Structure of BRCA1 BRCT D1840T in complex with a minimal recognition tetrapeptide with a free carboxy C-terminus\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3k0h\",\n"
+                + "        \"resolution\":2.7,\n"
+                + "        \"title\":\"The crystal structure of BRCA1 BRCT in complex with a minimal recognition tetrapeptide with an amidated C-terminus\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"4y18\",\n"
+                + "        \"resolution\":3.5,\n"
+                + "        \"title\":\"Structure of BRCA1 BRCT domains in complex with Abraxas double phosphorylated peptide\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"X-ray diffraction\"],\n"
+                + "        \"pdb_id\":\"3coj\",\n"
+                + "        \"resolution\":3.21,\n"
+                + "        \"title\":\"Crystal Structure of the BRCT Domains of Human BRCA1 in Complex with a Phosphorylated Peptide from Human Acetyl-CoA Carboxylase 1\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7jzv\",\n"
+                + "        \"resolution\":3.9,\n"
+                + "        \"title\":\"Cryo-EM structure of the BRCA1-UbcH5c/BARD1 E3-E2 module bound to a nucleosome\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7jzv\",\n"
+                + "        \"resolution\":3.9,\n"
+                + "        \"title\":\"Cryo-EM structure of the BRCA1-UbcH5c/BARD1 E3-E2 module bound to a nucleosome\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7lyb\",\n"
+                + "        \"resolution\":3.28,\n"
+                + "        \"title\":\"Cryo-EM structure of the human nucleosome core particle in complex with BRCA1-BARD1-UbcH5c\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7lyb\",\n"
+                + "        \"resolution\":3.28,\n"
+                + "        \"title\":\"Cryo-EM structure of the human nucleosome core particle in complex with BRCA1-BARD1-UbcH5c\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7lyb\",\n"
+                + "        \"resolution\":3.28,\n"
+                + "        \"title\":\"Cryo-EM structure of the human nucleosome core particle in complex with BRCA1-BARD1-UbcH5c\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7jzv\",\n"
+                + "        \"resolution\":3.9,\n"
+                + "        \"title\":\"Cryo-EM structure of the BRCA1-UbcH5c/BARD1 E3-E2 module bound to a nucleosome\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7lyb\",\n"
+                + "        \"resolution\":3.28,\n"
+                + "        \"title\":\"Cryo-EM structure of the human nucleosome core particle in complex with BRCA1-BARD1-UbcH5c\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7jzv\",\n"
+                + "        \"resolution\":3.9,\n"
+                + "        \"title\":\"Cryo-EM structure of the BRCA1-UbcH5c/BARD1 E3-E2 module bound to a nucleosome\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7lyb\",\n"
+                + "        \"resolution\":3.28,\n"
+                + "        \"title\":\"Cryo-EM structure of the human nucleosome core particle in complex with BRCA1-BARD1-UbcH5c\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7jzv\",\n"
+                + "        \"resolution\":3.9,\n"
+                + "        \"title\":\"Cryo-EM structure of the BRCA1-UbcH5c/BARD1 E3-E2 module bound to a nucleosome\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7lyb\",\n"
+                + "        \"resolution\":3.28,\n"
+                + "        \"title\":\"Cryo-EM structure of the human nucleosome core particle in complex with BRCA1-BARD1-UbcH5c\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7lyb\",\n"
+                + "        \"resolution\":3.28,\n"
+                + "        \"title\":\"Cryo-EM structure of the human nucleosome core particle in complex with BRCA1-BARD1-UbcH5c\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7lyb\",\n"
+                + "        \"resolution\":3.28,\n"
+                + "        \"title\":\"Cryo-EM structure of the human nucleosome core particle in complex with BRCA1-BARD1-UbcH5c\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7jzv\",\n"
+                + "        \"resolution\":3.9,\n"
+                + "        \"title\":\"Cryo-EM structure of the BRCA1-UbcH5c/BARD1 E3-E2 module bound to a nucleosome\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"6g2i\",\n"
+                + "        \"resolution\":5.9,\n"
+                + "        \"title\":\"Filament of acetyl-CoA carboxylase and BRCT domains of BRCA1 (ACC-BRCT) at 5.9 A resolution\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7jzv\",\n"
+                + "        \"resolution\":3.9,\n"
+                + "        \"title\":\"Cryo-EM structure of the BRCA1-UbcH5c/BARD1 E3-E2 module bound to a nucleosome\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7lyb\",\n"
+                + "        \"resolution\":3.28,\n"
+                + "        \"title\":\"Cryo-EM structure of the human nucleosome core particle in complex with BRCA1-BARD1-UbcH5c\"},\n"
+                + "      {\n"
+                + "        \"experimental_method\":[\"Electron Microscopy\"],\n"
+                + "        \"pdb_id\":\"7jzv\",\n"
+                + "        \"resolution\":3.9,\n"
+                + "        \"title\":\"Cryo-EM structure of the BRCA1-UbcH5c/BARD1 E3-E2 module bound to a nucleosome\"}]\n"
+                + "  }}" };
+    
+    mocks[1] = new String[2];
+    try {
+    mocks[1][0] = readJsonStringFromFile("test/jalview/fts/threedbeacons/p01308_pdbfts_query.txt").trim();
+    mocks[1][1] = readJsonStringFromFile("test/jalview/fts/threedbeacons/p01308_pdbfts_resp.txt").trim();
+    } catch (Throwable e)
+    {
+      Assert.fail("Couldn't read mock data.",e);
+    }
+    
+    FTSRestClient.createMockFTSRestClient((FTSRestClient) PDBFTSRestClient.getInstance(), mocks);
+  }
 }
diff --git a/test/jalview/fts/threedbeacons/.gitignore b/test/jalview/fts/threedbeacons/.gitignore
new file mode 100644 (file)
index 0000000..084aa65
--- /dev/null
@@ -0,0 +1,2 @@
+/tdbJson.java
+/tdbresttest2.java
diff --git a/test/jalview/fts/threedbeacons/TDBeaconsFTSRestClientTest.java b/test/jalview/fts/threedbeacons/TDBeaconsFTSRestClientTest.java
new file mode 100644 (file)
index 0000000..ef079d1
--- /dev/null
@@ -0,0 +1,453 @@
+package jalview.fts.threedbeacons;
+
+import static org.testng.Assert.assertNull;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import jalview.fts.api.FTSDataColumnI;
+import jalview.fts.api.FTSDataColumnI.FTSDataColumnGroupI;
+import jalview.fts.core.FTSRestClient;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.service.pdb.PDBFTSRestClientTest;
+import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient;
+import jalview.gui.JvOptionPane;
+
+public class TDBeaconsFTSRestClientTest
+{
+  @BeforeClass(alwaysRun = true)
+  public void setUpJvOptionPane()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+  }
+
+  private FTSRestClient ftsRestClient;
+
+  @BeforeMethod(alwaysRun = true)
+  public void setUp() throws Exception
+  {
+    ftsRestClient = new FTSRestClient()
+    {
+
+      @Override
+      public String getColumnDataConfigFileName()
+      {
+        return "/fts/tdbeacons_data_columns.txt";
+      }
+
+      @Override
+      public FTSRestResponse executeRequest(FTSRestRequest ftsRequest)
+              throws Exception
+      {
+        return null;
+      }
+    };
+  }
+
+  @AfterMethod(alwaysRun = true)
+  public void tearDown() throws Exception
+  {
+  }
+
+  @Test
+  public void getAllDefaulDisplayedDataColumns()
+  {
+    // to change when resources.tdbeacons_data_columns.txt is changed
+    Assert.assertNotNull(
+            ftsRestClient.getAllDefaultDisplayedFTSDataColumns());
+    System.out
+            .println(ftsRestClient.getAllDefaultDisplayedFTSDataColumns());
+    Assert.assertTrue(!ftsRestClient.getAllDefaultDisplayedFTSDataColumns()
+            .isEmpty());
+    Assert.assertEquals(
+            ftsRestClient.getAllDefaultDisplayedFTSDataColumns().size(),
+            14);
+  }
+
+  @Test(groups = { "Functional" })
+  public void getPrimaryKeyColumIndexTest()
+  {
+    Collection<FTSDataColumnI> wantedFields = ftsRestClient
+            .getAllDefaultDisplayedFTSDataColumns();
+    int foundIndex = -1;
+    try
+    {
+      Assert.assertEquals(foundIndex, -1);
+      foundIndex = ftsRestClient.getPrimaryKeyColumIndex(wantedFields,
+              false);
+      Assert.assertEquals(foundIndex, 12);
+      foundIndex = ftsRestClient.getPrimaryKeyColumIndex(wantedFields,
+              true);
+      // 1+primary key index
+      Assert.assertEquals(foundIndex, 13);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getDataColumnsFieldsAsCommaDelimitedString()
+  {
+    // to change when resources.tdbeacons_data_columns.txt is changed
+    Collection<FTSDataColumnI> wantedFields = ftsRestClient
+            .getAllDefaultDisplayedFTSDataColumns();
+    String actual = ftsRestClient
+            .getDataColumnsFieldsAsCommaDelimitedString(wantedFields);
+    Assert.assertEquals(actual,
+            "uniprot_start,uniprot_end,provider,model_identifier,model_category,model_title,resolution,confidence_avg_local_score,confidence_type,confidence_version,coverage,created,model_url,model_page_url");
+  }
+
+  @Test(groups = { "Functional" })
+  public void getAllFTSDataColumns()
+  {
+    Collection<FTSDataColumnI> allFields = ftsRestClient
+            .getAllFTSDataColumns();
+    Assert.assertNotNull(allFields);
+    // System.out.println(allFields.size());
+    Assert.assertEquals(allFields.size(), 19);
+  }
+
+  @Test(groups = { "Functional" })
+  public void getSearchableDataColumns()
+  {
+    // to change when resources.tdbeacons_data_columns.txt is changed
+    Collection<FTSDataColumnI> searchableFields = ftsRestClient
+            .getSearchableDataColumns();
+    Assert.assertNotNull(searchableFields);
+    // System.out.println(searchableFields.size());
+    Assert.assertEquals(searchableFields.size(), 1); // only 1: uniprot
+                                                     // accession
+  }
+
+  @Test(groups = { "Functional" })
+  public void getPrimaryKeyColumn()
+  {
+    // to change when resources.tdbeacons_data_columns.txt is changed
+    FTSDataColumnI expectedPKColumn;
+    try
+    {
+      expectedPKColumn = ftsRestClient.getDataColumnByNameOrCode("Url");
+      Assert.assertNotNull(ftsRestClient.getPrimaryKeyColumn());
+      Assert.assertEquals(ftsRestClient.getPrimaryKeyColumn(),
+              expectedPKColumn);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getDataColumnByNameOrCode()
+  {
+    try
+    {
+      FTSDataColumnI foundDataCol = ftsRestClient
+              .getDataColumnByNameOrCode("uniprot_accession");
+      Assert.assertNotNull(foundDataCol);
+      Assert.assertEquals(foundDataCol.getName(), "UniProt Accession");
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getDataColumnGroupById()
+  {
+    FTSDataColumnGroupI foundDataColGroup;
+    try
+    {
+      foundDataColGroup = ftsRestClient.getDataColumnGroupById("g2");
+      Assert.assertNotNull(foundDataColGroup);
+      Assert.assertEquals(foundDataColGroup.getName(), "Quality");
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void getDefaultResponsePageSize()
+  {
+    int defaultResSize = ftsRestClient.getDefaultResponsePageSize();
+    Assert.assertEquals(defaultResSize, 100); // why 100 or 500 ? pdb is 100,
+                                              // uniprot 500
+  }
+
+  @Test(groups = { "Functional" })
+  public void getColumnMinWidthTest()
+  {
+    try
+    {
+      FTSDataColumnI foundDataCol = ftsRestClient
+              .getDataColumnByNameOrCode("uniprot_accession");
+      Assert.assertNotNull(foundDataCol);
+      int actualColMinWidth = foundDataCol.getMinWidth();
+      Assert.assertEquals(actualColMinWidth, 50);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+  // could add test for MaxWidth & PreferedWith
+
+  @Test(groups = { "Functional" })
+  public void getColumnClassTest()
+  {
+    try
+    {
+      FTSDataColumnI foundDataCol = ftsRestClient
+              .getDataColumnByNameOrCode("uniprot_accession");
+      Assert.assertNotNull(foundDataCol);
+      Assert.assertEquals(foundDataCol.getDataType().getDataTypeClass(),
+              String.class);
+      foundDataCol = ftsRestClient.getDataColumnByNameOrCode("id");
+      Assert.assertNotNull(foundDataCol);
+      Assert.assertEquals(foundDataCol.getDataType().getDataTypeClass(),
+              String.class);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Exception thrown while testing...");
+    }
+  }
+
+  @Test(groups = { "Functional" })
+  public void coverageForEqualsAndHashFunction()
+  {
+    Set<FTSDataColumnI> uniqueSet = new HashSet<FTSDataColumnI>();
+    Collection<FTSDataColumnI> searchableCols = ftsRestClient
+            .getSearchableDataColumns();
+    System.out.println(searchableCols);
+    for (FTSDataColumnI foundCol : searchableCols)
+    {
+      System.out.println(foundCol.toString());
+      uniqueSet.add(foundCol);
+      uniqueSet.add(foundCol);
+    }
+    Assert.assertTrue(!uniqueSet.isEmpty());
+    // Assert.assertEquals(uniqueSet.size(), 22); -> 1 or 2 currently for 3DB
+  }
+
+  @Test(groups = { "Functional" })
+  public void getTDBIdColumIndexTest()
+  {
+    List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+    try
+    {
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("Model id"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("uniprot_accession"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("Title"));
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+    try
+    {
+      assertEquals(4, TDBeaconsFTSRestClient.getInstance()
+              .getPrimaryKeyColumIndex(wantedFields, true));
+      // assertEquals(3, TDBeaconsFTSRestClient.getInstance()
+      // .getPrimaryKeyColumIndex(wantedFields, true));
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+    }
+  }
+
+  private static String[][] mocks = { { "P38398.json",
+      "{\"uniprot_entry\":{\"sequence_length\":1863,\"ac\":\"P38398\",\"id\":\"BRCA1_HUMAN\"},\"structures\":[{\"model_identifier\":\"4igk\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2012-12-17\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":1.75,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/4igk_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/4igk\"},{\"model_identifier\":\"1t15\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2004-04-15\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":1.85,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/1t15_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/1t15\"},{\"model_identifier\":\"4ifi\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2012-12-14\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.2,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/4ifi_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/4ifi\"},{\"model_identifier\":\"1t29\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2004-04-20\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.3,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/1t29_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/1t29\"},{\"model_identifier\":\"3pxb\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2010-12-09\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.5,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/3pxb_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/3pxb\"},{\"model_identifier\":\"4y2g\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2015-02-09\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.5,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/4y2g_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/4y2g\"},{\"model_identifier\":\"1y98\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2004-12-14\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.5,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/1y98_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/1y98\"},{\"model_identifier\":\"1jnx\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2001-07-26\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.5,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/1jnx_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/1jnx\"},{\"model_identifier\":\"3pxa\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2010-12-09\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.55,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/3pxa_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/3pxa\"},{\"model_identifier\":\"3k0h\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2009-09-24\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.7,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/3k0h_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/3k0h\"},{\"model_identifier\":\"3k0k\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2009-09-24\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.7,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/3k0k_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/3k0k\"},{\"model_identifier\":\"1n5o\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2002-11-06\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.8,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/1n5o_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/1n5o\"},{\"model_identifier\":\"3pxc\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2010-12-09\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.8,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/3pxc_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/3pxc\"},{\"model_identifier\":\"3pxd\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2010-12-09\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.8,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/3pxd_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/3pxd\"},{\"model_identifier\":\"1t2u\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2004-04-22\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.8,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/1t2u_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/1t2u\"},{\"model_identifier\":\"3k15\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2009-09-25\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.8,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/3k15_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/3k15\"},{\"model_identifier\":\"3pxe\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2010-12-09\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":2.85,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/3pxe_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/3pxe\"},{\"model_identifier\":\"3k16\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2009-09-25\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":3.0,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/3k16_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/3k16\"},{\"model_identifier\":\"4ofb\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2014-01-14\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":3.05,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/4ofb_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/4ofb\"},{\"model_identifier\":\"3coj\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2008-03-28\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":3.21,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/3coj_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/3coj\"},{\"model_identifier\":\"7lyb\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2021-03-06\",\"sequence_identity\":100.0,\"uniprot_start\":1,\"uniprot_end\":100,\"resolution\":3.28,\"coverage\":5.37,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/7lyb_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"ELECTRON MICROSCOPY\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/7lyb\"},{\"model_identifier\":\"1t2v\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2004-04-22\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":3.3,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/1t2v_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/1t2v\"},{\"model_identifier\":\"4y18\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2015-02-06\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":3.5,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/4y18_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/4y18\"},{\"model_identifier\":\"4jlu\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2013-03-13\",\"sequence_identity\":100.0,\"uniprot_start\":1649,\"uniprot_end\":1859,\"resolution\":3.5,\"coverage\":11.33,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/4jlu_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/4jlu\"},{\"model_identifier\":\"4u4a\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2014-07-23\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":3.51,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/4u4a_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/4u4a\"},{\"model_identifier\":\"2ing\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2006-10-07\",\"sequence_identity\":100.0,\"uniprot_start\":1649,\"uniprot_end\":1859,\"resolution\":3.6,\"coverage\":11.33,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/2ing_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"X-RAY DIFFRACTION\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/2ing\"},{\"model_identifier\":\"7jzv\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2020-09-02\",\"sequence_identity\":99.0,\"uniprot_start\":2,\"uniprot_end\":104,\"resolution\":3.9,\"coverage\":5.53,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/7jzv_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"ELECTRON MICROSCOPY\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/7jzv\"},{\"model_identifier\":\"6g2i\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2018-03-23\",\"sequence_identity\":100.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"resolution\":5.9,\"coverage\":11.49,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/6g2i_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"ELECTRON MICROSCOPY\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/6g2i\"},{\"model_identifier\":\"1jm7\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2001-07-17\",\"sequence_identity\":100.0,\"uniprot_start\":1,\"uniprot_end\":110,\"resolution\":null,\"coverage\":5.9,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/1jm7_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"SOLUTION NMR\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/1jm7\"},{\"model_identifier\":\"1oqa\",\"model_category\":\"EXPERIMENTALLY DETERMINED\",\"provider\":\"PDBe\",\"created\":\"2003-03-07\",\"sequence_identity\":100.0,\"uniprot_start\":1755,\"uniprot_end\":1863,\"resolution\":null,\"coverage\":5.85,\"model_url\":\"https://www.ebi.ac.uk/pdbe/static/entry/1oqa_updated.cif\",\"model_format\":\"MMCIF\",\"experimental_method\":\"SOLUTION NMR\",\"model_page_url\":\"https://www.ebi.ac.uk/pdbe/entry/pdb/1oqa\"},{\"model_identifier\":\"614833627ca5054bb52d2f3f\",\"model_category\":\"TEMPLATE-BASED\",\"provider\":\"SWISS-MODEL\",\"created\":\"2021-09-20\",\"sequence_identity\":1.0,\"uniprot_start\":1646,\"uniprot_end\":1859,\"coverage\":0.115,\"confidence_version\":\"4.2.0\",\"confidence_avg_local_score\":0.776,\"model_url\":\"https://swissmodel.expasy.org/3d-beacons/uniprot/P38398.pdb?range=1646-1859&template=6g2i.1.K&provider=swissmodel\",\"model_format\":\"PDB\",\"model_page_url\":\"https://swissmodel.expasy.org/repository/uniprot/P38398?provider=swissmodelrange=1646-1859&template=6g2i.1.K\",\"confidence_type\":\"QMEANDisCo\"},{\"model_identifier\":\"614833627ca5054bb52d2f43\",\"model_category\":\"TEMPLATE-BASED\",\"provider\":\"SWISS-MODEL\",\"created\":\"2021-09-20\",\"sequence_identity\":1.0,\"uniprot_start\":1,\"uniprot_end\":103,\"coverage\":0.055,\"confidence_version\":\"4.2.0\",\"confidence_avg_local_score\":0.655,\"model_url\":\"https://swissmodel.expasy.org/3d-beacons/uniprot/P38398.pdb?range=1-103&template=1jm7.1.A&provider=swissmodel\",\"model_format\":\"PDB\",\"model_page_url\":\"https://swissmodel.expasy.org/repository/uniprot/P38398?provider=swissmodelrange=1-103&template=1jm7.1.A\",\"confidence_type\":\"QMEANDisCo\"},{\"model_identifier\":\"AF-P38398-F1\",\"model_category\":\"DEEP-LEARNING\",\"provider\":\"AlphaFold DB\",\"created\":\"2021-07-01\",\"sequence_identity\":1.0,\"uniprot_start\":1,\"uniprot_end\":1863,\"coverage\":100.0,\"model_url\":\"https://alphafold.ebi.ac.uk/files/AF-P38398-F1-model_v1.cif\",\"model_format\":\"MMCIF\",\"model_page_url\":\"https://alphafold.ebi.ac.uk/entry/P38398\"}]}" },
+      { "P01308.json",null}};
+
+  private static void setMockData()
+  {
+    try
+    {
+      mocks[1][1]= PDBFTSRestClientTest.readJsonStringFromFile("test/jalview/fts/threedbeacons/p01308_tdb_resp.txt");
+    } catch (IOException e)
+    {
+      Assert.fail("Couldn't read mock response data",e);
+    }
+  }
+  
+  public static void setMock()
+  {
+    setMockData();
+    FTSRestClient.createMockFTSRestClient(
+            (FTSRestClient) TDBeaconsFTSRestClient.getInstance(),
+            mocks);
+  }
+
+  private static String dev_url = "https://wwwdev.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/api/uniprot/summary/";
+  private static String prod_url = "https://www.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/api/uniprot/summary/";
+
+  /**
+   * check that the mock request and response are the same as the response from
+   * a live 3D-beacons endpoint
+   * 
+   * Note - servers often have rapidly changing ids / URIs so this might fail, but the overall structure will remain. 
+   * 
+   * @throws Exception
+   */
+  @Test(groups = { "Network", "Integration" })
+  public void verifyMockTDBRequest() throws Exception
+  {
+    setMockData();
+    for (String[] otherMock:mocks)
+    {
+    verifyMockTDBRequest(otherMock[0],otherMock[1]);
+    }
+  }
+  private void verifyMockTDBRequest(String mockRequest, String _mockResponse) throws Exception
+  {
+    URL tdb_req = new URL(prod_url + mockRequest);
+    byte[] resp = tdb_req.openStream().readAllBytes();
+    String tresp = new String(resp, StandardCharsets.UTF_8);
+    assertEquals(_mockResponse.trim(), tresp.trim());
+  }
+
+  @Test(groups = { "Functional" })
+  public void testMockTDBRequest()
+  {
+
+    setMock();
+    List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+    try
+    {
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("Model Id"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("model_url"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("provider"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("model_category"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("qmean_avg_local_score"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("uniprot_start"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("uniprot_end"));
+    } catch (Exception e1)
+    {
+      e1.printStackTrace();
+    }
+    System.out.println("wantedFields >>" + wantedFields);
+
+    FTSRestRequest request = new FTSRestRequest();
+    FTSRestResponse response;
+
+    request.setResponseSize(100);
+    request.setFieldToSearchBy("");
+    request.setWantedFields(wantedFields);
+    // check 404 behaviour
+    request.setSearchTerm("P00000.json");
+
+    try
+    {
+      response = TDBeaconsFTSRestClient.getInstance()
+              .executeRequest(request);
+
+      assertNull(response);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Unexpected failure during mock 3DBeacons 404 test");
+    }
+
+    // check 200 behaviour
+    request.setSearchTerm("P38398.json");
+    System.out.println("request : " + request.getFieldToSearchBy());
+    // System.out.println(request.toString());
+
+    try
+    {
+      response = TDBeaconsFTSRestClient.getInstance()
+              .executeRequest(request);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Couldn't execute webservice call!");
+      return;
+    }
+    assertTrue(response.getSearchSummary() != null);
+    assertTrue(response.getNumberOfItemsFound() > 3); // 4 atm
+    System.out.println("Search summary : \n" + response.getSearchSummary());
+
+    // System.out.println(response.getSearchSummary().size());
+  }
+
+  @Test(groups = { "External", "Network" })
+  public void executeRequestTest()
+  {
+    List<FTSDataColumnI> wantedFields = new ArrayList<FTSDataColumnI>();
+    try
+    {
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("Model Id"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("model_url"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("provider"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("model_category"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("confidence_avg_local_score"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("uniprot_start"));
+      wantedFields.add(TDBeaconsFTSRestClient.getInstance()
+              .getDataColumnByNameOrCode("uniprot_end"));
+    } catch (Exception e1)
+    {
+      e1.printStackTrace();
+    }
+    System.out.println("wantedFields >>" + wantedFields);
+
+    FTSRestRequest request = new FTSRestRequest();
+    request.setResponseSize(100);
+    request.setFieldToSearchBy("");
+    request.setSearchTerm("P01318.json");
+    request.setWantedFields(wantedFields);
+    System.out.println("request : " + request.getFieldToSearchBy());
+    // System.out.println(request.toString());
+
+    FTSRestResponse response;
+    try
+    {
+      response = TDBeaconsFTSRestClient.getInstance()
+              .executeRequest(request);
+    } catch (Exception e)
+    {
+      e.printStackTrace();
+      Assert.fail("Couldn't execute webservice call!");
+      return;
+    }
+    assertTrue(response.getSearchSummary() != null);
+    assertTrue(response.getNumberOfItemsFound() > 3); // 4 atm
+    System.out.println("Search summary : \n" + response.getSearchSummary());
+    // System.out.println(response.getSearchSummary().size());
+  }
+}
diff --git a/test/jalview/fts/threedbeacons/TDBeaconsPanelTest.java b/test/jalview/fts/threedbeacons/TDBeaconsPanelTest.java
new file mode 100644 (file)
index 0000000..dec33e3
--- /dev/null
@@ -0,0 +1,68 @@
+package jalview.fts.threedbeacons;
+
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
+
+import jalview.fts.service.pdb.PDBFTSPanel;
+import jalview.fts.service.threedbeacons.TDBeaconsFTSPanel;
+import jalview.gui.JvOptionPane;
+
+import javax.swing.JComboBox;
+import javax.swing.JInternalFrame;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import junit.extensions.PA;
+
+
+public class TDBeaconsPanelTest
+{
+  @BeforeClass(alwaysRun = true)
+  public void setUpJvOptionPane()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+  }
+
+  @BeforeMethod(alwaysRun = true)
+  public void setUp() throws Exception
+  {
+  }
+
+  @AfterMethod(alwaysRun = true)
+  public void tearDown() throws Exception
+  {
+  }
+
+  @Test(groups = { "Functional" })
+  public void populateCmbSearchTargetOptionsTest()
+  {
+    TDBeaconsFTSPanel searchPanel = new TDBeaconsFTSPanel(null);
+    assertTrue(searchPanel.getCmbSearchTarget().getItemCount() > 0);
+    searchPanel.populateCmbSearchTargetOptions();
+  }
+  
+  @Test
+  public void getFTSframeTitleTest() {
+    TDBeaconsFTSPanel searchPanel = new TDBeaconsFTSPanel(null);
+    System.out.println(searchPanel.getFTSFrameTitle());
+  }
+  
+  @Test
+  public void testgetUNIPROTid() {
+    String outcome = TDBeaconsFTSPanel.decodeSearchTerm("P01308");
+    System.out.println(outcome);
+  }
+//  
+//  @Test
+//  public void queryTest() {
+//    int outcome = TDBeaconsFTSPanel.executeParse("P01308");
+//    //System.out.println("query outcome :" + outcome);
+//    int expected_length = 110;
+//    assertEquals(outcome, expected_length);
+//  }
+}
diff --git a/test/jalview/fts/threedbeacons/p01308_pdbfts_query.txt b/test/jalview/fts/threedbeacons/p01308_pdbfts_query.txt
new file mode 100644 (file)
index 0000000..60fd3cd
--- /dev/null
@@ -0,0 +1,2 @@
+https://www.ebi.ac.uk/pdbe/search/pdb/select?wt=json&fl=pdb_id,title,experimental_method,resolution&rows=500&start=0&q=(3w7y+OR+3w7y+OR+5e7w+OR+5e7w+OR+5hqi+OR+5hqi+OR+1mso+OR+1mso+OR+3hyd+OR+6ver+OR+6ver+OR+4fka+OR+4fka+OR+3tt8+OR+3tt8+OR+3w7z+OR+3w7z+OR+5usp+OR+5usp+OR+5uoz+OR+5uoz+OR+5urt+OR+5urt+OR+1g7a+OR+4ajx+OR+1g7a+OR+4ajx+OR+5uu2+OR+5uu2+OR+6gv0+OR+6gv0+OR+5hrq+OR+5hrq+OR+1g7b+OR+5usv+OR+3bxq+OR+1g7b+OR+5usv+OR+3bxq+OR+5uqa+OR+5uqa+OR+5hpr+OR+5hpr+OR+5udp+OR+5udp+OR+3fq9+OR+5ena+OR+3exx+OR+6s34+OR+5ena+OR+3exx+OR+6s34+OR+3fq9+OR+6tc2+OR+2omh+OR+2omh+OR+4nib+OR+4cy7+OR+3w80+OR+1ben+OR+7nhu+OR+4nib+OR+4cy7+OR+7nhu+OR+3w80+OR+1ben+OR+6vet+OR+6vet+OR+4ey1+OR+4ey9+OR+4eyd+OR+4ey1+OR+4ey9+OR+4eyd+OR+4xc4+OR+4xc4+OR+1zeh+OR+2wrx+OR+2ws6+OR+2ws6+OR+4cxl+OR+5en9+OR+4p65+OR+2c8r+OR+6s4j+OR+1zeh+OR+2wrx+OR+2ws6+OR+2c8r+OR+4cxl+OR+5en9+OR+4p65+OR+6s4j+OR+6s4i+OR+6s4i+OR+2omg+OR+2omg+OR+4eyn+OR+4eyn+OR+5bqq+OR+5bqq+OR+4exx+OR+5t7r+OR+4exx+OR+5t7r+OR+2wru+OR+2wru+OR+6o17+OR+6o17+OR+4une+OR+4une+OR+4eyp+OR+4f1b+OR+4eyp+OR+4f1b+OR+1zeg+OR+2ws1+OR+3zu1+OR+1trz+OR+4iuz+OR+3i3z+OR+1uz9+OR+3e7y+OR+3i3z+OR+1zeg+OR+2ws1+OR+1uz9+OR+3zu1+OR+3e7y+OR+1trz+OR+4iuz+OR+6tyh+OR+6tyh+OR+6nwv+OR+6nwv+OR+1guj+OR+1guj+OR+4f1d+OR+4f1g+OR+4f4t+OR+4f4v+OR+4f51+OR+4f1d+OR+4f1g+OR+4f4t+OR+4f4v+OR+4f51+OR+4ex1+OR+4ex1+OR+4iyd+OR+4iyd+OR+3utq+OR+4f0o+OR+4f0o+OR+4f8f+OR+4f8f+OR+4f0n+OR+4f0n+OR+5c0d+OR+4f1f+OR+4f1f+OR+3zi3+OR+4f1c+OR+4cxn+OR+5cny+OR+5co2+OR+2oly+OR+2olz+OR+5boq+OR+5viz+OR+3e7z+OR+3zi3+OR+4f1c+OR+4cxn+OR+5cny+OR+3e7z+OR+5co2+OR+2oly+OR+2olz+OR+5boq+OR+5viz+OR+4rxw+OR+4rxw+OR+5bts+OR+5bts+OR+4gbc+OR+4gbc+OR+1ev3+OR+1ev3+OR+4ewz+OR+4ewz+OR+4f1a+OR+2vjz+OR+1rwe+OR+5co6+OR+6p4z+OR+1xda+OR+4ajz+OR+4iyf+OR+2ceu+OR+4f1a+OR+2ceu+OR+2vjz+OR+1rwe+OR+1xda+OR+5co6+OR+4ajz+OR+6p4z+OR+4iyf+OR+4ung+OR+4ung+OR+6h3m+OR+6h3m+OR+3inc+OR+3i40+OR+6ves+OR+3i40+OR+6ves+OR+3inc+OR+4z77+OR+4ex0+OR+4ex0+OR+4gbn+OR+4gbn+OR+5mt9+OR+5mt9+OR+4z76+OR+3ilg+OR+3zqr+OR+3kq6+OR+1ev6+OR+1evr+OR+1tyl+OR+1tym+OR+5bpo+OR+1htv+OR+2ws4+OR+1htv+OR+2ws4+OR+3ilg+OR+3zqr+OR+3kq6+OR+1ev6+OR+1evr+OR+1tyl+OR+1tym+OR+5bpo+OR+5co9+OR+5co9+OR+1os3+OR+2c8q+OR+2c8q+OR+1os3+OR+2om1+OR+3zs2+OR+2om1+OR+3zs2+OR+5uu4+OR+5uu4+OR+3u4n+OR+4efx+OR+3u4n+OR+4efx+OR+1q4v+OR+3p2x+OR+2qiu+OR+3v19+OR+1j73+OR+1qiz+OR+1znj+OR+2r36+OR+2w44+OR+2qiu+OR+2r36+OR+1q4v+OR+3p2x+OR+3v19+OR+1j73+OR+1qiz+OR+1znj+OR+2w44+OR+2omq+OR+4fg3+OR+4fg3+OR+4akj+OR+4akj+OR+5mt3+OR+5mt3+OR+2om0+OR+3q6e+OR+2om0+OR+3q6e+OR+5uss+OR+5uss+OR+1w8p+OR+2r35+OR+2r35+OR+1w8p+OR+2ws0+OR+2ws0+OR+2wrv+OR+2wrv+OR+6z7y+OR+5mam+OR+3v1g+OR+2vk0+OR+3ir0+OR+5hpu+OR+2g56+OR+6gnq+OR+6z7y+OR+5mam+OR+3v1g+OR+2vk0+OR+3ir0+OR+5hpu+OR+6gnq+OR+4ewx+OR+4ewx+OR+2omi+OR+2omi+OR+5uu3+OR+1os4+OR+2g54+OR+6ck2+OR+2r34+OR+2r34+OR+5uu3+OR+1os4+OR+6ck2+OR+4ak0+OR+4ak0+OR+3p33+OR+5ems+OR+1qiy+OR+1lph+OR+3rov+OR+4eww+OR+1xw7+OR+3p33+OR+5ems+OR+1qiy+OR+1lph+OR+3rov+OR+4eww+OR+1xw7+OR+4z78+OR+4wdi+OR+1qj0+OR+4gbk+OR+1qj0+OR+4gbk+OR+1jk8+OR+5uru+OR+2wrw+OR+2wrw+OR+5uru+OR+6z7w+OR+6z7w+OR+1jca+OR+3jsd+OR+1b9e+OR+4gbl+OR+1jca+OR+3jsd+OR+1b9e+OR+4gbl+OR+4y19+OR+4gbi+OR+4gbi+OR+2ws7+OR+2ws7+OR+2wby+OR+2wby+OR+3utt+OR+3uts+OR+4unh+OR+4unh+OR+2wc0+OR+2wc0+OR+5wdm+OR+6vep+OR+6vep+OR+5hyj+OR+6hn5+OR+6hn5+OR+5cjo+OR+4oga+OR+4oga+OR+6b70+OR+6b3q+OR+6bfc+OR+7bw8+OR+3w11+OR+3w11+OR+5wob+OR+4y1a+OR+7bw7+OR+6sof+OR+6ce9+OR+6sof+OR+3w12+OR+3w12+OR+3w13+OR+3w13+OR+6jk8+OR+6ceb+OR+7bwa+OR+6ce7+OR+6jr3+OR+6jr3+OR+2kqp+OR+1efe+OR+6u46+OR+1sju+OR+5mwq+OR+6k59+OR+1t0c+OR+5mhd+OR+2lgb+OR+2mvd+OR+2mvc+OR+1k3m+OR+2juu+OR+1hiq+OR+2juv+OR+2rn5+OR+2kxk+OR+2hiu+OR+2l1y+OR+2l1z+OR+2kju+OR+1xgl+OR+1jco+OR+2jv1+OR+2kqq+OR+1fu2+OR+1kmf+OR+1vkt+OR+1fub+OR+2n2x+OR+2n2v+OR+2n2w+OR+1t1k+OR+1t1p+OR+1t1q+OR+2mpg+OR+1sf1+OR+2mpi+OR+2jmn+OR+2k9r+OR+6x4x+OR+1lkq+OR+2m1d+OR+2m1e+OR+2mli+OR+2hh4+OR+2m2m+OR+2aiy+OR+1hit+OR+2hho+OR+1hls+OR+2m2n+OR+2m2o+OR+2m2p+OR+3aiy+OR+4aiy+OR+5aiy+OR+2kjj+OR+2k91+OR+1ai0+OR+1aiy+OR+2h67+OR+1mhi+OR+2jum+OR+1sjt+OR+1a7f+OR+1iog+OR+1ioh+OR+1mhj+OR+1hui+OR+1his+OR+2mvc+OR+2mvd+OR+1k3m+OR+2juu+OR+1hiq+OR+2juv+OR+2rn5+OR+2kxk+OR+2hiu+OR+2l1z+OR+2l1y+OR+1sjt+OR+2kju+OR+1xgl+OR+1jco+OR+2jv1+OR+1a7f+OR+1iog+OR+1ioh+OR+2kqq+OR+1fu2+OR+1kmf+OR+1vkt+OR+1fub+OR+2n2v+OR+2n2w+OR+2n2x+OR+1t1k+OR+1t1p+OR+1t1q+OR+2mpg+OR+1sf1+OR+5mwq+OR+2mpi+OR+2jmn+OR+2k9r+OR+6x4x+OR+5mhd+OR+1lkq+OR+2lgb+OR+2m1d+OR+2m1e+OR+2mli+OR+2hh4+OR+2m2m+OR+2aiy+OR+1his+OR+1hit+OR+2hho+OR+1hls+OR+2m2n+OR+2m2o+OR+2m2p+OR+3aiy+OR+4aiy+OR+5aiy+OR+1hui+OR+2kjj+OR+2k91+OR+1ai0+OR+1aiy+OR+2h67+OR+1mhi+OR+1mhj+OR+2jum+OR+6k59)+AND+molecule_sequence:%5B''+TO+*%5D+AND+status:REL&sort=
+
diff --git a/test/jalview/fts/threedbeacons/p01308_pdbfts_resp.txt b/test/jalview/fts/threedbeacons/p01308_pdbfts_resp.txt
new file mode 100644 (file)
index 0000000..8d362ea
--- /dev/null
@@ -0,0 +1,2381 @@
+{
+  "responseHeader":{
+    "status":0,
+    "QTime":6,
+    "params":{
+      "q":"(3w7y OR 3w7y OR 5e7w OR 5e7w OR 5hqi OR 5hqi OR 1mso OR 1mso OR 3hyd OR 6ver OR 6ver OR 4fka OR 4fka OR 3tt8 OR 3tt8 OR 3w7z OR 3w7z OR 5usp OR 5usp OR 5uoz OR 5uoz OR 5urt OR 5urt OR 1g7a OR 4ajx OR 1g7a OR 4ajx OR 5uu2 OR 5uu2 OR 6gv0 OR 6gv0 OR 5hrq OR 5hrq OR 1g7b OR 5usv OR 3bxq OR 1g7b OR 5usv OR 3bxq OR 5uqa OR 5uqa OR 5hpr OR 5hpr OR 5udp OR 5udp OR 3fq9 OR 5ena OR 3exx OR 6s34 OR 5ena OR 3exx OR 6s34 OR 3fq9 OR 6tc2 OR 2omh OR 2omh OR 4nib OR 4cy7 OR 3w80 OR 1ben OR 7nhu OR 4nib OR 4cy7 OR 7nhu OR 3w80 OR 1ben OR 6vet OR 6vet OR 4ey1 OR 4ey9 OR 4eyd OR 4ey1 OR 4ey9 OR 4eyd OR 4xc4 OR 4xc4 OR 1zeh OR 2wrx OR 2ws6 OR 2ws6 OR 4cxl OR 5en9 OR 4p65 OR 2c8r OR 6s4j OR 1zeh OR 2wrx OR 2ws6 OR 2c8r OR 4cxl OR 5en9 OR 4p65 OR 6s4j OR 6s4i OR 6s4i OR 2omg OR 2omg OR 4eyn OR 4eyn OR 5bqq OR 5bqq OR 4exx OR 5t7r OR 4exx OR 5t7r OR 2wru OR 2wru OR 6o17 OR 6o17 OR 4une OR 4une OR 4eyp OR 4f1b OR 4eyp OR 4f1b OR 1zeg OR 2ws1 OR 3zu1 OR 1trz OR 4iuz OR 3i3z OR 1uz9 OR 3e7y OR 3i3z OR 1zeg OR 2ws1 OR 1uz9 OR 3zu1 OR 3e7y OR 1trz OR 4iuz OR 6tyh OR 6tyh OR 6nwv OR 6nwv OR 1guj OR 1guj OR 4f1d OR 4f1g OR 4f4t OR 4f4v OR 4f51 OR 4f1d OR 4f1g OR 4f4t OR 4f4v OR 4f51 OR 4ex1 OR 4ex1 OR 4iyd OR 4iyd OR 3utq OR 4f0o OR 4f0o OR 4f8f OR 4f8f OR 4f0n OR 4f0n OR 5c0d OR 4f1f OR 4f1f OR 3zi3 OR 4f1c OR 4cxn OR 5cny OR 5co2 OR 2oly OR 2olz OR 5boq OR 5viz OR 3e7z OR 3zi3 OR 4f1c OR 4cxn OR 5cny OR 3e7z OR 5co2 OR 2oly OR 2olz OR 5boq OR 5viz OR 4rxw OR 4rxw OR 5bts OR 5bts OR 4gbc OR 4gbc OR 1ev3 OR 1ev3 OR 4ewz OR 4ewz OR 4f1a OR 2vjz OR 1rwe OR 5co6 OR 6p4z OR 1xda OR 4ajz OR 4iyf OR 2ceu OR 4f1a OR 2ceu OR 2vjz OR 1rwe OR 1xda OR 5co6 OR 4ajz OR 6p4z OR 4iyf OR 4ung OR 4ung OR 6h3m OR 6h3m OR 3inc OR 3i40 OR 6ves OR 3i40 OR 6ves OR 3inc OR 4z77 OR 4ex0 OR 4ex0 OR 4gbn OR 4gbn OR 5mt9 OR 5mt9 OR 4z76 OR 3ilg OR 3zqr OR 3kq6 OR 1ev6 OR 1evr OR 1tyl OR 1tym OR 5bpo OR 1htv OR 2ws4 OR 1htv OR 2ws4 OR 3ilg OR 3zqr OR 3kq6 OR 1ev6 OR 1evr OR 1tyl OR 1tym OR 5bpo OR 5co9 OR 5co9 OR 1os3 OR 2c8q OR 2c8q OR 1os3 OR 2om1 OR 3zs2 OR 2om1 OR 3zs2 OR 5uu4 OR 5uu4 OR 3u4n OR 4efx OR 3u4n OR 4efx OR 1q4v OR 3p2x OR 2qiu OR 3v19 OR 1j73 OR 1qiz OR 1znj OR 2r36 OR 2w44 OR 2qiu OR 2r36 OR 1q4v OR 3p2x OR 3v19 OR 1j73 OR 1qiz OR 1znj OR 2w44 OR 2omq OR 4fg3 OR 4fg3 OR 4akj OR 4akj OR 5mt3 OR 5mt3 OR 2om0 OR 3q6e OR 2om0 OR 3q6e OR 5uss OR 5uss OR 1w8p OR 2r35 OR 2r35 OR 1w8p OR 2ws0 OR 2ws0 OR 2wrv OR 2wrv OR 6z7y OR 5mam OR 3v1g OR 2vk0 OR 3ir0 OR 5hpu OR 2g56 OR 6gnq OR 6z7y OR 5mam OR 3v1g OR 2vk0 OR 3ir0 OR 5hpu OR 6gnq OR 4ewx OR 4ewx OR 2omi OR 2omi OR 5uu3 OR 1os4 OR 2g54 OR 6ck2 OR 2r34 OR 2r34 OR 5uu3 OR 1os4 OR 6ck2 OR 4ak0 OR 4ak0 OR 3p33 OR 5ems OR 1qiy OR 1lph OR 3rov OR 4eww OR 1xw7 OR 3p33 OR 5ems OR 1qiy OR 1lph OR 3rov OR 4eww OR 1xw7 OR 4z78 OR 4wdi OR 1qj0 OR 4gbk OR 1qj0 OR 4gbk OR 1jk8 OR 5uru OR 2wrw OR 2wrw OR 5uru OR 6z7w OR 6z7w OR 1jca OR 3jsd OR 1b9e OR 4gbl OR 1jca OR 3jsd OR 1b9e OR 4gbl OR 4y19 OR 4gbi OR 4gbi OR 2ws7 OR 2ws7 OR 2wby OR 2wby OR 3utt OR 3uts OR 4unh OR 4unh OR 2wc0 OR 2wc0 OR 5wdm OR 6vep OR 6vep OR 5hyj OR 6hn5 OR 6hn5 OR 5cjo OR 4oga OR 4oga OR 6b70 OR 6b3q OR 6bfc OR 7bw8 OR 3w11 OR 3w11 OR 5wob OR 4y1a OR 7bw7 OR 6sof OR 6ce9 OR 6sof OR 3w12 OR 3w12 OR 3w13 OR 3w13 OR 6jk8 OR 6ceb OR 7bwa OR 6ce7 OR 6jr3 OR 6jr3 OR 2kqp OR 1efe OR 6u46 OR 1sju OR 5mwq OR 6k59 OR 1t0c OR 5mhd OR 2lgb OR 2mvd OR 2mvc OR 1k3m OR 2juu OR 1hiq OR 2juv OR 2rn5 OR 2kxk OR 2hiu OR 2l1y OR 2l1z OR 2kju OR 1xgl OR 1jco OR 2jv1 OR 2kqq OR 1fu2 OR 1kmf OR 1vkt OR 1fub OR 2n2x OR 2n2v OR 2n2w OR 1t1k OR 1t1p OR 1t1q OR 2mpg OR 1sf1 OR 2mpi OR 2jmn OR 2k9r OR 6x4x OR 1lkq OR 2m1d OR 2m1e OR 2mli OR 2hh4 OR 2m2m OR 2aiy OR 1hit OR 2hho OR 1hls OR 2m2n OR 2m2o OR 2m2p OR 3aiy OR 4aiy OR 5aiy OR 2kjj OR 2k91  OR 1ai0 OR 1aiy OR 2h67 OR 1mhi OR 2jum OR 1sjt OR 1a7f OR 1iog OR 1ioh OR 1mhj OR 1hui OR 1his OR 2mvc OR 2mvd OR 1k3m OR 2juu OR 1hiq OR 2juv OR 2rn5 OR 2kxk OR 2hiu OR 2l1z OR 2l1y OR 1sjt OR 2kju OR 1xgl OR 1jco OR 2jv1 OR 1a7f OR 1iog OR 1ioh OR 2kqq OR 1fu2 OR 1kmf OR 1vkt OR 1fub OR 2n2v OR 2n2w OR 2n2x OR 1t1k OR 1t1p OR 1t1q OR 2mpg OR 1sf1 OR 5mwq OR 2mpi OR 2jmn OR 2k9r OR 6x4x OR 5mhd OR 1lkq OR 2lgb OR 2m1d OR 2m1e OR 2mli OR 2hh4 OR 2m2m OR 2aiy OR 1his OR 1hit OR 2hho OR 1hls OR 2m2n OR 2m2o OR 2m2p OR 3aiy OR 4aiy OR 5aiy OR 1hui OR 2kjj OR 2k91 OR 1ai0 OR 1aiy OR 2h67 OR 1mhi OR 1mhj OR 2jum OR 6k59) AND molecule_sequence:['' TO *] AND status:REL",
+      "fl":"pdb_id,title,experimental_method,resolution",
+      "start":"0",
+      "sort":"",
+      "rows":"500",
+      "wt":"json"}},
+  "response":{"numFound":638,"start":0,"docs":[
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws6",
+        "resolution":1.5,
+        "title":"Semi-synthetic analogue of human insulin NMeTyrB26-insulin in hexamer form"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws6",
+        "resolution":1.5,
+        "title":"Semi-synthetic analogue of human insulin NMeTyrB26-insulin in hexamer form"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws6",
+        "resolution":1.5,
+        "title":"Semi-synthetic analogue of human insulin NMeTyrB26-insulin in hexamer form"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1hit",
+        "title":"Receptor binding redefined by a structural switch in a mutant Human Insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1his",
+        "title":"Structure and dynamics of des-pentapeptide-insulin in solution: the molten-globule hypothesis."},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1his",
+        "title":"Structure and dynamics of des-pentapeptide-insulin in solution: the molten-globule hypothesis."},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"6k59",
+        "title":"Structure of Glargine insulin in 20% acetic acid-d4 (pH 1.9)"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"6k59",
+        "title":"Structure of Glargine insulin in 20% acetic acid-d4 (pH 1.9)"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1hit",
+        "title":"Receptor binding redefined by a structural switch in a mutant Human Insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m2o",
+        "title":"Structure of [D-HisB24] insulin analogue at pH 1.9"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m1d",
+        "title":"Biosynthetic engineered B28K-B29P human insulin monomer structure in in water/acetonitrile solutions."},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m2n",
+        "title":"Structure of [L-HisB24] insulin analogue at pH 8.0"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m2n",
+        "title":"Structure of [L-HisB24] insulin analogue at pH 8.0"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1sjt",
+        "title":"MINI-PROINSULIN, TWO CHAIN INSULIN ANALOG MUTANT: DES B30, HIS(B 10)ASP, PRO(B 28)ASP, NMR, 20 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1vkt",
+        "title":"HUMAN INSULIN TWO DISULFIDE MODEL, NMR, 10 STRUCTURES"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2c8r",
+        "resolution":1.5,
+        "title":"insuline(60sec) and UV laser excited fluorescence"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2c8r",
+        "resolution":1.5,
+        "title":"insuline(60sec) and UV laser excited fluorescence"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1hiq",
+        "title":"PARADOXICAL STRUCTURE AND FUNCTION IN A MUTANT HUMAN INSULIN ASSOCIATED WITH DIABETES MELLITUS"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1hiq",
+        "title":"PARADOXICAL STRUCTURE AND FUNCTION IN A MUTANT HUMAN INSULIN ASSOCIATED WITH DIABETES MELLITUS"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2c8q",
+        "resolution":1.95,
+        "title":"insuline(1sec) and UV laser excited fluorescence"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2c8q",
+        "resolution":1.95,
+        "title":"insuline(1sec) and UV laser excited fluorescence"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6o17",
+        "resolution":1.58,
+        "title":"Recombinant Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6o17",
+        "resolution":1.58,
+        "title":"Recombinant Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"7nhu",
+        "resolution":1.4,
+        "title":"Crystal structure of desB30 insulin produced by cell free protein synthesis"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"7nhu",
+        "resolution":1.4,
+        "title":"Crystal structure of desB30 insulin produced by cell free protein synthesis"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2mvd",
+        "title":"Solution structure of [GlnB22]-insulin mutant at pH 1.9"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2mvd",
+        "title":"Solution structure of [GlnB22]-insulin mutant at pH 1.9"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2mvc",
+        "title":"Solution structure of human insulin at pH 1.9"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m1e",
+        "title":"Biosynthetic engineered B28K-B29P human insulin monomer structure in in water solutions."},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m2p",
+        "title":"Structure of [D-HisB24] insulin analogue at pH 8.0"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws4",
+        "resolution":1.9,
+        "title":"Semi-synthetic analogue of human insulin ProB26-DTI in monomer form"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m2o",
+        "title":"Structure of [D-HisB24] insulin analogue at pH 1.9"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m1d",
+        "title":"Biosynthetic engineered B28K-B29P human insulin monomer structure in in water/acetonitrile solutions."},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m1e",
+        "title":"Biosynthetic engineered B28K-B29P human insulin monomer structure in in water solutions."},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1sjt",
+        "title":"MINI-PROINSULIN, TWO CHAIN INSULIN ANALOG MUTANT: DES B30, HIS(B 10)ASP, PRO(B 28)ASP, NMR, 20 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1vkt",
+        "title":"HUMAN INSULIN TWO DISULFIDE MODEL, NMR, 10 STRUCTURES"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3ilg",
+        "resolution":1.9,
+        "title":"Crystal structure of humnan insulin Sr+2 complex"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1lkq",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT ILE-A2-GLY, VAL-A3-GLY, HIS-B10-ASP, PRO-B28-LYS, LYS-B29-PRO, 20 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1k3m",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT ILE-A2-ALA, HIS-B10-ASP, PRO-B28-LYS, LYS-B29-PRO, 15 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1k3m",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT ILE-A2-ALA, HIS-B10-ASP, PRO-B28-LYS, LYS-B29-PRO, 15 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1lkq",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT ILE-A2-GLY, VAL-A3-GLY, HIS-B10-ASP, PRO-B28-LYS, LYS-B29-PRO, 20 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"5mwq",
+        "title":"Biosynthetic engineered A21K-B31K-B32R human insulin monomer structure in water/acetonitrile solution"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5ena",
+        "resolution":1.35,
+        "title":"Xray crystal structure of isotope-labeled human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5viz",
+        "resolution":1.7,
+        "title":"X-Ray structure of Insulin Glargine"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5viz",
+        "resolution":1.7,
+        "title":"X-Ray structure of Insulin Glargine"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2mvc",
+        "title":"Solution structure of human insulin at pH 1.9"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5en9",
+        "resolution":1.5,
+        "title":"High resolution x-ray crystal structure of isotope-labeled ester-insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m2p",
+        "title":"Structure of [D-HisB24] insulin analogue at pH 8.0"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2jum",
+        "title":"ThrA3-DKP-insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2jum",
+        "title":"ThrA3-DKP-insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2kjj",
+        "title":"Dynamics of insulin probed by 1H-NMR amide proton exchange anomalous flexibility of the receptor-binding surface"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2kjj",
+        "title":"Dynamics of insulin probed by 1H-NMR amide proton exchange anomalous flexibility of the receptor-binding surface"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2jv1",
+        "title":"NMR structure of human insulin monomer in 35% CD3CN zinc free, 50 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2kju",
+        "title":"NMR structure of human insulin mutant glu-b21-d-glu, his-b10 asp pro-b28-lys, lys-b29-pro, 20 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2juu",
+        "title":"allo-ThrA3 DKP-insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wrw",
+        "resolution":2.41,
+        "title":"Semi-synthetic highly active analogue of human insulin D-ProB26-DTI- NH2"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wrw",
+        "resolution":2.41,
+        "title":"Semi-synthetic highly active analogue of human insulin D-ProB26-DTI- NH2"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws4",
+        "resolution":1.9,
+        "title":"Semi-synthetic analogue of human insulin ProB26-DTI in monomer form"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2r34",
+        "resolution":2.25,
+        "title":"Crystal structure of MN human arg-insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2l1z",
+        "title":"NMR Structure of human insulin mutant GLY-B20-D-ALA, GLY-B23-D-ALA PRO-B28-LYS, LYS-B29-PRO, 20 Structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2l1z",
+        "title":"NMR Structure of human insulin mutant GLY-B20-D-ALA, GLY-B23-D-ALA PRO-B28-LYS, LYS-B29-PRO, 20 Structures"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2qiu",
+        "resolution":2.0,
+        "title":"Structure of Human Arg-Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1os4",
+        "resolution":2.25,
+        "title":"Dehydrated T6 human insulin at 295 K"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1t1k",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT HIS-B10-ASP, VAL-B12-ALA, PRO-B28-LYS, LYS-B29-PRO, 15 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1t1k",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT HIS-B10-ASP, VAL-B12-ALA, PRO-B28-LYS, LYS-B29-PRO, 15 STRUCTURES"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3ilg",
+        "resolution":1.9,
+        "title":"Crystal structure of humnan insulin Sr+2 complex"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3i40",
+        "resolution":1.85,
+        "title":"Human insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1xgl",
+        "title":"HUMAN INSULIN DISULFIDE ISOMER, NMR, 10 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1xgl",
+        "title":"HUMAN INSULIN DISULFIDE ISOMER, NMR, 10 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1iog",
+        "title":"INSULIN MUTANT A3 GLY,(B1, B10, B16, B27)GLU, DES-B30, NMR, 19 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1jco",
+        "title":"Solution structure of the monomeric [Thr(B27)->Pro,Pro(B28)->Thr] insulin mutant (PT insulin)"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1kmf",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT ILE-A2-ALLO-ILE, HIS-B10-ASP, PRO-B28-LYS, LYS-B29-PRO, 15 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1kmf",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT ILE-A2-ALLO-ILE, HIS-B10-ASP, PRO-B28-LYS, LYS-B29-PRO, 15 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1hui",
+        "title":"INSULIN MUTANT (B1, B10, B16, B27)GLU, DES-B30, NMR, 25 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1hui",
+        "title":"INSULIN MUTANT (B1, B10, B16, B27)GLU, DES-B30, NMR, 25 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1a7f",
+        "title":"INSULIN MUTANT B16 GLU, B24 GLY, DES-B30, NMR, 20 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1ioh",
+        "title":"INSULIN MUTANT A8 HIS,(B1, B10, B16, B27)GLU, DES-B30, NMR, 26 STRUCTURES"},
+      {
+        "experimental_method":["Electron Microscopy"],
+        "pdb_id":"6jr3",
+        "resolution":14.5,
+        "title":"Crystal structure of insulin hexamer fitted into cryo EM density map where each dimer was kept as rigid body"},
+      {
+        "experimental_method":["Electron Microscopy"],
+        "pdb_id":"6jr3",
+        "resolution":14.5,
+        "title":"Crystal structure of insulin hexamer fitted into cryo EM density map where each dimer was kept as rigid body"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"5mwq",
+        "title":"Biosynthetic engineered A21K-B31K-B32R human insulin monomer structure in water/acetonitrile solution"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"6x4x",
+        "title":"B24Y DKP insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"6x4x",
+        "title":"B24Y DKP insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5t7r",
+        "resolution":1.55,
+        "title":"A6-A11 trans-dicarba human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5t7r",
+        "resolution":1.55,
+        "title":"A6-A11 trans-dicarba human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5ena",
+        "resolution":1.35,
+        "title":"Xray crystal structure of isotope-labeled human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3zi3",
+        "resolution":1.7,
+        "title":"Crystal structure of the B24His-insulin - human analogue"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3zi3",
+        "resolution":1.7,
+        "title":"Crystal structure of the B24His-insulin - human analogue"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3u4n",
+        "resolution":1.98,
+        "title":"A novel covalently linked insulin dimer"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3u4n",
+        "resolution":1.98,
+        "title":"A novel covalently linked insulin dimer"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2r35",
+        "resolution":2.08,
+        "title":"Crystal structure of RB human arg-insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5en9",
+        "resolution":1.5,
+        "title":"High resolution x-ray crystal structure of isotope-labeled ester-insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2h67",
+        "title":"NMR structure of human insulin mutant HIS-B5-ALA, HIS-B10-ASP PRO-B28-LYS, LYS-B29-PRO, 20 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2kju",
+        "title":"NMR structure of human insulin mutant glu-b21-d-glu, his-b10 asp pro-b28-lys, lys-b29-pro, 20 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2hho",
+        "title":"NMR structure of human insulin mutant GLY-B8-SER, HIS-B10-ASP PRO-B28-LYS, LYS-B29-PRO, 20 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2juv",
+        "title":"AbaA3-DKP-insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2juv",
+        "title":"AbaA3-DKP-insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2jv1",
+        "title":"NMR structure of human insulin monomer in 35% CD3CN zinc free, 50 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2juu",
+        "title":"allo-ThrA3 DKP-insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2h67",
+        "title":"NMR structure of human insulin mutant HIS-B5-ALA, HIS-B10-ASP PRO-B28-LYS, LYS-B29-PRO, 20 structures"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws0",
+        "resolution":2.1,
+        "title":"Semi-synthetic analogue of human insulin NMeAlaB26-insulin at pH 7.5"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2rn5",
+        "title":"Humal Insulin Mutant B31Lys-B32Arg"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2rn5",
+        "title":"Humal Insulin Mutant B31Lys-B32Arg"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2qiu",
+        "resolution":2.0,
+        "title":"Structure of Human Arg-Insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2lgb",
+        "title":"Modified A22Gly-B31Arg Human Insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2lgb",
+        "title":"Modified A22Gly-B31Arg Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1os4",
+        "resolution":2.25,
+        "title":"Dehydrated T6 human insulin at 295 K"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1t1p",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT HIS-B10-ASP, VAL-B12-THR, PRO-B28-LYS, LYS-B29-PRO, 15 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1t1p",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT HIS-B10-ASP, VAL-B12-THR, PRO-B28-LYS, LYS-B29-PRO, 15 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2aiy",
+        "title":"R6 HUMAN INSULIN HEXAMER (SYMMETRIC), NMR, 20 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1sf1",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN under Amyloidogenic Condition, 15 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1sf1",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN under Amyloidogenic Condition, 15 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1t1q",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT HIS-B10-ASP, VAL-B12-ABA, PRO-B28-LYS, LYS-B29-PRO, 15 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1t1q",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN MUTANT HIS-B10-ASP, VAL-B12-ABA, PRO-B28-LYS, LYS-B29-PRO, 15 STRUCTURES"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3i3z",
+        "resolution":1.6,
+        "title":"Human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3i3z",
+        "resolution":1.6,
+        "title":"Human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3i40",
+        "resolution":1.85,
+        "title":"Human insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1iog",
+        "title":"INSULIN MUTANT A3 GLY,(B1, B10, B16, B27)GLU, DES-B30, NMR, 19 STRUCTURES"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1mso",
+        "resolution":1.0,
+        "title":"T6 Human Insulin at 1.0 A Resolution"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1jco",
+        "title":"Solution structure of the monomeric [Thr(B27)->Pro,Pro(B28)->Thr] insulin mutant (PT insulin)"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1a7f",
+        "title":"INSULIN MUTANT B16 GLU, B24 GLY, DES-B30, NMR, 20 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1hls",
+        "title":"NMR STRUCTURE OF THE HUMAN INSULIN-HIS(B16)"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1hls",
+        "title":"NMR STRUCTURE OF THE HUMAN INSULIN-HIS(B16)"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1ioh",
+        "title":"INSULIN MUTANT A8 HIS,(B1, B10, B16, B27)GLU, DES-B30, NMR, 26 STRUCTURES"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4iyd",
+        "resolution":1.66,
+        "title":"Insulin glargine crystal structure 1"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4iyd",
+        "resolution":1.66,
+        "title":"Insulin glargine crystal structure 1"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6s34",
+        "resolution":1.35,
+        "title":"Zinc free, dimeric human insulin determined to 1.35 Angstrom resolution"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6s34",
+        "resolution":1.35,
+        "title":"Zinc free, dimeric human insulin determined to 1.35 Angstrom resolution"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6ves",
+        "resolution":1.85,
+        "title":"Human insulin analog: [GluB10,HisA8,ArgA9]-DOI"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6ves",
+        "resolution":1.85,
+        "title":"Human insulin analog: [GluB10,HisA8,ArgA9]-DOI"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4unh",
+        "resolution":2.75,
+        "title":"Human insulin B26Gly mutant crystal structure"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4unh",
+        "resolution":2.75,
+        "title":"Human insulin B26Gly mutant crystal structure"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2mpg",
+        "title":"Solution structure of the [AibB8,LysB28,ProB29]-insulin analogue"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3w7y",
+        "resolution":0.92,
+        "title":"0.92A structure of 2Zn human insulin at 100K"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3w7y",
+        "resolution":0.92,
+        "title":"0.92A structure of 2Zn human insulin at 100K"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3w7z",
+        "resolution":1.15,
+        "title":"1.15A structure of human 2Zn insulin at 293K"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3w7z",
+        "resolution":1.15,
+        "title":"1.15A structure of human 2Zn insulin at 293K"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ak0",
+        "resolution":2.28,
+        "title":"Ligand controlled assembly of hexamers, dihexamers, and linear multihexamer structures by an engineered acylated insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ak0",
+        "resolution":2.28,
+        "title":"Ligand controlled assembly of hexamers, dihexamers, and linear multihexamer structures by an engineered acylated insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uoz",
+        "resolution":1.1746387,
+        "title":"Insulin with proline analog FyP at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uoz",
+        "resolution":1.1746387,
+        "title":"Insulin with proline analog FyP at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ung",
+        "resolution":1.81,
+        "title":"Human insulin B26Asn mutant crystal structure"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2r35",
+        "resolution":2.08,
+        "title":"Crystal structure of RB human arg-insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2hiu",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN IN 20% ACETIC ACID, ZINC-FREE, 10 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2hiu",
+        "title":"NMR STRUCTURE OF HUMAN INSULIN IN 20% ACETIC ACID, ZINC-FREE, 10 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m2m",
+        "title":"Structure of [L-HisB24] insulin analogue at pH 1.9"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2m2m",
+        "title":"Structure of [L-HisB24] insulin analogue at pH 1.9"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wrx",
+        "resolution":1.5,
+        "title":"Semi-synthetic analogue of human insulin NMeAlaB26-insulin at pH 3.0"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wrx",
+        "resolution":1.5,
+        "title":"Semi-synthetic analogue of human insulin NMeAlaB26-insulin at pH 3.0"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2hho",
+        "title":"NMR structure of human insulin mutant GLY-B8-SER, HIS-B10-ASP PRO-B28-LYS, LYS-B29-PRO, 20 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2hh4",
+        "title":"NMR structure of human insulin mutant GLY-B8-D-SER, HIS-B10-ASP PRO-B28-LYS, LYS-B29-PRO, 20 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2hh4",
+        "title":"NMR structure of human insulin mutant GLY-B8-D-SER, HIS-B10-ASP PRO-B28-LYS, LYS-B29-PRO, 20 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2kxk",
+        "title":"Human Insulin Mutant A22Gly-B31Lys-B32Arg"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2kxk",
+        "title":"Human Insulin Mutant A22Gly-B31Lys-B32Arg"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4cxn",
+        "resolution":1.7,
+        "title":"Crystal structure of human insulin analogue (NMe-AlaB8)-insulin crystal form I"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4cxn",
+        "resolution":1.7,
+        "title":"Crystal structure of human insulin analogue (NMe-AlaB8)-insulin crystal form I"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws0",
+        "resolution":2.1,
+        "title":"Semi-synthetic analogue of human insulin NMeAlaB26-insulin at pH 7.5"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2kqq",
+        "title":"NMR structure of human insulin mutant gly-b8-d-ala, his-b10-asp, pro-b28-lys, lys-b29-pro, 20 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2l1y",
+        "title":"NMR Structure of human insulin mutant GLY-B20-D-ALA, GLY-B23-D-ALA PRO-B28-LYS, LYS-B29-PRO, 20 Structures"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1os3",
+        "resolution":1.95,
+        "title":"Dehydrated T6 human insulin at 100 K"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1os3",
+        "resolution":1.95,
+        "title":"Dehydrated T6 human insulin at 100 K"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"3aiy",
+        "title":"R6 HUMAN INSULIN HEXAMER (SYMMETRIC), NMR, REFINED AVERAGE STRUCTURE"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"3aiy",
+        "title":"R6 HUMAN INSULIN HEXAMER (SYMMETRIC), NMR, REFINED AVERAGE STRUCTURE"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2aiy",
+        "title":"R6 HUMAN INSULIN HEXAMER (SYMMETRIC), NMR, 20 STRUCTURES"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3inc",
+        "resolution":1.85,
+        "title":"Crystal structure of human insulin with Ni+2 complex"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3inc",
+        "resolution":1.85,
+        "title":"Crystal structure of human insulin with Ni+2 complex"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1mso",
+        "resolution":1.0,
+        "title":"T6 Human Insulin at 1.0 A Resolution"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1mhi",
+        "title":"THREE-DIMENSIONAL SOLUTION STRUCTURE OF AN INSULIN DIMER. A STUDY OF THE B9(ASP) MUTANT OF HUMAN INSULIN USING NUCLEAR MAGNETIC RESONANCE DISTANCE GEOMETRY AND RESTRAINED MOLECULAR DYNAMICS"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1mhi",
+        "title":"THREE-DIMENSIONAL SOLUTION STRUCTURE OF AN INSULIN DIMER. A STUDY OF THE B9(ASP) MUTANT OF HUMAN INSULIN USING NUCLEAR MAGNETIC RESONANCE DISTANCE GEOMETRY AND RESTRAINED MOLECULAR DYNAMICS"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1mhj",
+        "title":"SOLUTION STRUCTURE OF THE SUPERACTIVE MONOMERIC DES-[PHE(B25)] HUMAN INSULIN MUTANT. ELUCIDATION OF THE STRUCTURAL BASIS FOR THE MONOMERIZATION OF THE DES-[PHE(B25)] INSULIN AND THE DIMERIZATION OF NATIVE INSULIN"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1mhj",
+        "title":"SOLUTION STRUCTURE OF THE SUPERACTIVE MONOMERIC DES-[PHE(B25)] HUMAN INSULIN MUTANT. ELUCIDATION OF THE STRUCTURAL BASIS FOR THE MONOMERIZATION OF THE DES-[PHE(B25)] INSULIN AND THE DIMERIZATION OF NATIVE INSULIN"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1aiy",
+        "title":"R6 HUMAN INSULIN HEXAMER (SYMMETRIC), NMR, 10 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2k91",
+        "title":"Enhancing the activity of insulin by stereospecific unfolding"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2k91",
+        "title":"Enhancing the activity of insulin by stereospecific unfolding"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4iyf",
+        "resolution":1.8,
+        "title":"Insulin glargine crystal structure 2"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4iyf",
+        "resolution":1.8,
+        "title":"Insulin glargine crystal structure 2"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"5aiy",
+        "title":"R6 HUMAN INSULIN HEXAMER (SYMMETRIC), NMR, 'RED' SUBSTATE, AVERAGE STRUCTURE"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"5aiy",
+        "title":"R6 HUMAN INSULIN HEXAMER (SYMMETRIC), NMR, 'RED' SUBSTATE, AVERAGE STRUCTURE"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4une",
+        "resolution":1.59,
+        "title":"Human insulin B26Phe mutant crystal structure"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4une",
+        "resolution":1.59,
+        "title":"Human insulin B26Phe mutant crystal structure"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5hqi",
+        "resolution":0.97,
+        "title":"Insulin with proline analog HzP at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5hqi",
+        "resolution":0.97,
+        "title":"Insulin with proline analog HzP at position B28 in the T2 state"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2mli",
+        "title":"NMR structure of B25-(alpha, beta)-dehydro-phenylalanine insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2mpg",
+        "title":"Solution structure of the [AibB8,LysB28,ProB29]-insulin analogue"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"4aiy",
+        "title":"R6 HUMAN INSULIN HEXAMER (SYMMETRIC), NMR, 'GREEN' SUBSTATE, AVERAGE STRUCTURE"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"4aiy",
+        "title":"R6 HUMAN INSULIN HEXAMER (SYMMETRIC), NMR, 'GREEN' SUBSTATE, AVERAGE STRUCTURE"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ewx",
+        "resolution":2.201,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4eww",
+        "resolution":2.3,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3w80",
+        "resolution":1.4,
+        "title":"Crystal structure of dodecamer human insulin with double C-axis length of the hexamer 2 Zn insulin cell"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3w80",
+        "resolution":1.4,
+        "title":"Crystal structure of dodecamer human insulin with double C-axis length of the hexamer 2 Zn insulin cell"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3tt8",
+        "resolution":1.12,
+        "title":"Crystal Structure Analysis of Cu Human Insulin Derivative"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3tt8",
+        "resolution":1.12,
+        "title":"Crystal Structure Analysis of Cu Human Insulin Derivative"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2n2v",
+        "title":"Solution structure of [B26-B29 triazole cross-linked]-insulin analogue at pH 1.9"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2n2w",
+        "title":"Solution structure of [B26-B29 triazole cross-linked]-insulin analogue at pH 8.0"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2n2x",
+        "title":"Solution structure of [GlyB24,B27-B29 triazole cross-linked]-insulin analogue at pH 1.9"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ung",
+        "resolution":1.81,
+        "title":"Human insulin B26Asn mutant crystal structure"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2r34",
+        "resolution":2.25,
+        "title":"Crystal structure of MN human arg-insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2mpi",
+        "title":"Solution structure of B24G insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2mpi",
+        "title":"Solution structure of B24G insulin"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"5mhd",
+        "title":"Biosynthetic engineered A22S-B3K-B31R human insulin monomer structure in water/acetonitrile solutions."},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"5mhd",
+        "title":"Biosynthetic engineered A22S-B3K-B31R human insulin monomer structure in water/acetonitrile solutions."},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws1",
+        "resolution":1.6,
+        "title":"Semi-synthetic analogue of human insulin NMeTyrB26-insulin in monomer form"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws1",
+        "resolution":1.6,
+        "title":"Semi-synthetic analogue of human insulin NMeTyrB26-insulin in monomer form"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1c",
+        "resolution":1.7,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1d",
+        "resolution":1.637,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f0o",
+        "resolution":1.672,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1g",
+        "resolution":1.637,
+        "title":"Human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f4v",
+        "resolution":1.637,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f51",
+        "resolution":1.637,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f8f",
+        "resolution":1.676,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1f",
+        "resolution":1.684,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wru",
+        "resolution":1.57,
+        "title":"Semi-synthetic highly active analogue of human insulin NMeAlaB26-DTI- NH2"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wru",
+        "resolution":1.57,
+        "title":"Semi-synthetic highly active analogue of human insulin NMeAlaB26-DTI- NH2"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2kqq",
+        "title":"NMR structure of human insulin mutant gly-b8-d-ala, his-b10-asp, pro-b28-lys, lys-b29-pro, 20 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2l1y",
+        "title":"NMR Structure of human insulin mutant GLY-B20-D-ALA, GLY-B23-D-ALA PRO-B28-LYS, LYS-B29-PRO, 20 Structures"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1a",
+        "resolution":1.8,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1a",
+        "resolution":1.8,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f0n",
+        "resolution":1.679,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f0n",
+        "resolution":1.679,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f4t",
+        "resolution":1.637,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1b",
+        "resolution":1.591,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1jca",
+        "resolution":2.5,
+        "title":"Non-standard Design of Unstable Insulin Analogues with Enhanced Activity"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3bxq",
+        "resolution":1.3,
+        "title":"The structure of a mutant insulin uncouples receptor binding from protein allostery. An electrostatic block to the TR transition"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1htv",
+        "resolution":1.9,
+        "title":"CRYSTAL STRUCTURE OF DESTRIPEPTIDE (B28-B30) INSULIN"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1htv",
+        "resolution":1.9,
+        "title":"CRYSTAL STRUCTURE OF DESTRIPEPTIDE (B28-B30) INSULIN"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1ai0",
+        "title":"R6 HUMAN INSULIN HEXAMER (NON-SYMMETRIC), NMR, 10 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1ai0",
+        "title":"R6 HUMAN INSULIN HEXAMER (NON-SYMMETRIC), NMR, 10 STRUCTURES"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"1aiy",
+        "title":"R6 HUMAN INSULIN HEXAMER (SYMMETRIC), NMR, 10 STRUCTURES"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5bts",
+        "resolution":1.77,
+        "title":"Structural and biophysical characterization of a covalent insulin dimer formed during storage of neutral formulation of human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5bts",
+        "resolution":1.77,
+        "title":"Structural and biophysical characterization of a covalent insulin dimer formed during storage of neutral formulation of human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5usp",
+        "resolution":1.174,
+        "title":"Insulin with proline analog Pip at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5usp",
+        "resolution":1.174,
+        "title":"Insulin with proline analog Pip at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5bpo",
+        "resolution":1.9,
+        "title":"Human insulin with intra-chain chemical crosslink between modified B27 and B29"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uu2",
+        "resolution":1.223,
+        "title":"Insulin with proline analog ThioP at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5urt",
+        "resolution":1.18,
+        "title":"Insulin with proline analog DhP at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6h3m",
+        "resolution":1.821,
+        "title":"The crystal structure of a human seleno-insulin analog"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6h3m",
+        "resolution":1.821,
+        "title":"The crystal structure of a human seleno-insulin analog"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6ver",
+        "resolution":1.047,
+        "title":"Human insulin analog: [GluB10,TyrB20]-DOI"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6ver",
+        "resolution":1.047,
+        "title":"Human insulin analog: [GluB10,TyrB20]-DOI"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ewx",
+        "resolution":2.201,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ey1",
+        "resolution":1.471,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4eww",
+        "resolution":2.3,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4exx",
+        "resolution":1.55,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4exx",
+        "resolution":1.55,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4eyp",
+        "resolution":1.591,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ewz",
+        "resolution":1.791,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ewz",
+        "resolution":1.791,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ex0",
+        "resolution":1.86,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ex0",
+        "resolution":1.86,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4eyd",
+        "resolution":1.471,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4eyd",
+        "resolution":1.471,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ex1",
+        "resolution":1.657,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3q6e",
+        "resolution":2.05,
+        "title":"Human insulin in complex with cucurbit[7]uril"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3q6e",
+        "resolution":2.05,
+        "title":"Human insulin in complex with cucurbit[7]uril"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2n2v",
+        "title":"Solution structure of [B26-B29 triazole cross-linked]-insulin analogue at pH 1.9"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2n2w",
+        "title":"Solution structure of [B26-B29 triazole cross-linked]-insulin analogue at pH 8.0"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2n2x",
+        "title":"Solution structure of [GlyB24,B27-B29 triazole cross-linked]-insulin analogue at pH 1.9"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ey9",
+        "resolution":1.471,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ey9",
+        "resolution":1.471,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4fka",
+        "resolution":1.08,
+        "title":"High resolution structure of the manganese derivative of insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4eyn",
+        "resolution":1.532,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4eyn",
+        "resolution":1.532,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4rxw",
+        "resolution":1.73,
+        "title":"Crystal Structure of the cobalt human insulin derivative"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4rxw",
+        "resolution":1.73,
+        "title":"Crystal Structure of the cobalt human insulin derivative"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5usv",
+        "resolution":1.3,
+        "title":"Insulin with proline analog AzeP at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5usv",
+        "resolution":1.3,
+        "title":"Insulin with proline analog AzeP at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wrv",
+        "resolution":2.15,
+        "title":"Semi-synthetic highly active analogue of human insulin NMeHisB26-DTI- NH2"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wrv",
+        "resolution":2.15,
+        "title":"Semi-synthetic highly active analogue of human insulin NMeHisB26-DTI- NH2"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2vk0",
+        "resolution":2.2,
+        "title":"Crystal structure form ultalente insulin microcrystals"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3exx",
+        "resolution":1.35,
+        "title":"Structure of the T6 human insulin derivative with nickel at 1.35 A resolution"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3exx",
+        "resolution":1.35,
+        "title":"Structure of the T6 human insulin derivative with nickel at 1.35 A resolution"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2vjz",
+        "resolution":1.8,
+        "title":"Crystal structure form ultalente insulin microcrystals"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1c",
+        "resolution":1.7,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1d",
+        "resolution":1.637,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f0o",
+        "resolution":1.672,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4gbc",
+        "resolution":1.778,
+        "title":"Crystal structure of aspart insulin at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4gbn",
+        "resolution":1.872,
+        "title":"Crystal structure of aspart insulin at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1g",
+        "resolution":1.637,
+        "title":"Human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f4v",
+        "resolution":1.637,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f51",
+        "resolution":1.637,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1f",
+        "resolution":1.684,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f8f",
+        "resolution":1.676,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4gbi",
+        "resolution":2.502,
+        "title":"Crystal structure of aspart insulin at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4cxl",
+        "resolution":1.5,
+        "title":"Human insulin analogue (D-ProB8)-insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4cxl",
+        "resolution":1.5,
+        "title":"Human insulin analogue (D-ProB8)-insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2r36",
+        "resolution":2.0,
+        "title":"Crystal structure of ni human ARG-insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f4t",
+        "resolution":1.637,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4f1b",
+        "resolution":1.591,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3ir0",
+        "resolution":2.2,
+        "title":"Crystal Structure of Human Insulin complexed with Cu+2 metal ion"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3ir0",
+        "resolution":2.2,
+        "title":"Crystal Structure of Human Insulin complexed with Cu+2 metal ion"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1jca",
+        "resolution":2.5,
+        "title":"Non-standard Design of Unstable Insulin Analogues with Enhanced Activity"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3bxq",
+        "resolution":1.3,
+        "title":"The structure of a mutant insulin uncouples receptor binding from protein allostery. An electrostatic block to the TR transition"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1b9e",
+        "resolution":2.5,
+        "title":"HUMAN INSULIN MUTANT SERB9GLU"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1b9e",
+        "resolution":2.5,
+        "title":"HUMAN INSULIN MUTANT SERB9GLU"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ceu",
+        "resolution":1.8,
+        "title":"Despentapeptide insulin in acetic acid (pH 2)"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ceu",
+        "resolution":1.8,
+        "title":"Despentapeptide insulin in acetic acid (pH 2)"},
+      {
+        "experimental_method":["X-ray powder diffraction"],
+        "pdb_id":"1fub",
+        "title":"FIRST PROTEIN STRUCTURE DETERMINED FROM X-RAY POWDER DIFFRACTION DATA"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5bpo",
+        "resolution":1.9,
+        "title":"Human insulin with intra-chain chemical crosslink between modified B27 and B29"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uu2",
+        "resolution":1.223,
+        "title":"Insulin with proline analog ThioP at position B28 in the T2 state"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2mli",
+        "title":"NMR structure of B25-(alpha, beta)-dehydro-phenylalanine insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ey1",
+        "resolution":1.471,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4eyp",
+        "resolution":1.591,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ex1",
+        "resolution":1.657,
+        "title":"Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4iuz",
+        "resolution":1.6,
+        "title":"High resolution crystal structure of racemic ester insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5urt",
+        "resolution":1.18,
+        "title":"Insulin with proline analog DhP at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4fka",
+        "resolution":1.08,
+        "title":"High resolution structure of the manganese derivative of insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5cny",
+        "resolution":1.7,
+        "title":"Crystal Structure of human zinc insulin at pH 5.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2vk0",
+        "resolution":2.2,
+        "title":"Crystal structure form ultalente insulin microcrystals"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2k9r",
+        "title":"Enhancing the activity of insulin by stereospecific unfolding"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2k9r",
+        "title":"Enhancing the activity of insulin by stereospecific unfolding"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2omi",
+        "resolution":2.24,
+        "title":"Structure of human insulin cocrystallized with protamine"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2vjz",
+        "resolution":1.8,
+        "title":"Crystal structure form ultalente insulin microcrystals"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4gbn",
+        "resolution":1.872,
+        "title":"Crystal structure of aspart insulin at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4gbl",
+        "resolution":2.5,
+        "title":"Crystal structure of aspart insulin at pH 8.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4gbl",
+        "resolution":2.5,
+        "title":"Crystal structure of aspart insulin at pH 8.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4gbi",
+        "resolution":2.502,
+        "title":"Crystal structure of aspart insulin at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4cy7",
+        "resolution":1.4,
+        "title":"Crystal structure of human insulin analogue (NMe-AlaB8)-insulin crystal form II"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2r36",
+        "resolution":2.0,
+        "title":"Crystal structure of ni human ARG-insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4gbc",
+        "resolution":1.778,
+        "title":"Crystal structure of aspart insulin at pH 6.5"},
+      {
+        "experimental_method":["X-ray powder diffraction"],
+        "pdb_id":"1fu2",
+        "title":"FIRST PROTEIN STRUCTURE DETERMINED FROM X-RAY POWDER DIFFRACTION DATA"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1ev3",
+        "resolution":1.78,
+        "title":"Structure of the rhombohedral form of the M-cresol/insulin R6 hexamer"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1j73",
+        "resolution":2.0,
+        "title":"Crystal structure of an unstable insulin analog with native activity."},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1j73",
+        "resolution":2.0,
+        "title":"Crystal structure of an unstable insulin analog with native activity."},
+      {
+        "experimental_method":["X-ray powder diffraction"],
+        "pdb_id":"1fub",
+        "title":"FIRST PROTEIN STRUCTURE DETERMINED FROM X-RAY POWDER DIFFRACTION DATA"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5co2",
+        "resolution":1.7,
+        "title":"Crystalization of human zinc insulin at pH 5.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5co2",
+        "resolution":1.7,
+        "title":"Crystalization of human zinc insulin at pH 5.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5boq",
+        "resolution":1.7,
+        "title":"Human insulin with intra-chain chemical crosslink between modified B24 and B29"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5boq",
+        "resolution":1.7,
+        "title":"Human insulin with intra-chain chemical crosslink between modified B24 and B29"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6s4i",
+        "resolution":1.511,
+        "title":"Crystal structure of zinc free A14E, B25H, B29K(N(eps)-[2-(2-[2-(2-[2-(Octadecandioyl-gamma-Glu)amino]ethoxy)ethoxy]acetylamino)ethoxy]ethoxy)acetyl]), desB30 human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6s4i",
+        "resolution":1.511,
+        "title":"Crystal structure of zinc free A14E, B25H, B29K(N(eps)-[2-(2-[2-(2-[2-(Octadecandioyl-gamma-Glu)amino]ethoxy)ethoxy]acetylamino)ethoxy]ethoxy)acetyl]), desB30 human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6p4z",
+        "resolution":1.8,
+        "title":"Structure of gadolinium-caged cobalt (III) insulin hexamer"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6gv0",
+        "resolution":1.26,
+        "title":"Insulin glulisine"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6vet",
+        "resolution":1.46,
+        "title":"Human insulin analog: [GluB10,HisA8,ArgA9,TyrB20]-DOI"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6vet",
+        "resolution":1.46,
+        "title":"Human insulin analog: [GluB10,HisA8,ArgA9,TyrB20]-DOI"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4iuz",
+        "resolution":1.6,
+        "title":"High resolution crystal structure of racemic ester insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4gbk",
+        "resolution":2.4,
+        "title":"Crystal structure of aspart insulin at pH 8.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4gbk",
+        "resolution":2.4,
+        "title":"Crystal structure of aspart insulin at pH 8.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4efx",
+        "resolution":1.98,
+        "title":"Highly biologically active insulin with additional disulfide bond"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4efx",
+        "resolution":1.98,
+        "title":"Highly biologically active insulin with additional disulfide bond"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5co9",
+        "resolution":1.92,
+        "title":"Crystal structure of human zinc insulin at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5cny",
+        "resolution":1.7,
+        "title":"Crystal Structure of human zinc insulin at pH 5.5"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2jmn",
+        "title":"NMR structure of human insulin mutant His-B10-Asp, Pro-B28-Lys, Lys-B29-Pro, 20 structures"},
+      {
+        "experimental_method":["Solution NMR"],
+        "pdb_id":"2jmn",
+        "title":"NMR structure of human insulin mutant His-B10-Asp, Pro-B28-Lys, Lys-B29-Pro, 20 structures"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3e7z",
+        "resolution":1.7,
+        "title":"Structure of human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3e7z",
+        "resolution":1.7,
+        "title":"Structure of human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2w44",
+        "resolution":2.0,
+        "title":"Structure DeltaA1-A4 insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3e7y",
+        "resolution":1.6,
+        "title":"Structure of human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3e7y",
+        "resolution":1.6,
+        "title":"Structure of human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2omi",
+        "resolution":2.24,
+        "title":"Structure of human insulin cocrystallized with protamine"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws7",
+        "resolution":2.59,
+        "title":"Semi-synthetic analogue of human insulin ProB26-DTI"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4cy7",
+        "resolution":1.4,
+        "title":"Crystal structure of human insulin analogue (NMe-AlaB8)-insulin crystal form II"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4nib",
+        "resolution":1.4,
+        "title":"Crystal structure of human insulin mutant B20 D-ala, B23 D-ala"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4nib",
+        "resolution":1.4,
+        "title":"Crystal structure of human insulin mutant B20 D-ala, B23 D-ala"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1qj0",
+        "resolution":2.4,
+        "title":"HUMAN INSULIN HEXAMERS WITH CHAIN B HIS MUTATED TO TYR"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1uz9",
+        "resolution":1.6,
+        "title":"Crystallographic and solution studies of N-lithocholyl insulin: a new generation of prolonged-acting insulins."},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1uz9",
+        "resolution":1.6,
+        "title":"Crystallographic and solution studies of N-lithocholyl insulin: a new generation of prolonged-acting insulins."},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1trz",
+        "resolution":1.6,
+        "title":"CRYSTALLOGRAPHIC EVIDENCE FOR DUAL COORDINATION AROUND ZINC IN THE T3R3 HUMAN INSULIN HEXAMER"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1trz",
+        "resolution":1.6,
+        "title":"CRYSTALLOGRAPHIC EVIDENCE FOR DUAL COORDINATION AROUND ZINC IN THE T3R3 HUMAN INSULIN HEXAMER"},
+      {
+        "experimental_method":["X-ray powder diffraction"],
+        "pdb_id":"1fu2",
+        "title":"FIRST PROTEIN STRUCTURE DETERMINED FROM X-RAY POWDER DIFFRACTION DATA"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1evr",
+        "resolution":1.9,
+        "title":"The structure of the resorcinol/insulin R6 hexamer"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1ev3",
+        "resolution":1.78,
+        "title":"Structure of the rhombohedral form of the M-cresol/insulin R6 hexamer"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1guj",
+        "resolution":1.62,
+        "title":"Insulin at pH 2: structural analysis of the conditions promoting insulin fibre formation."},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1guj",
+        "resolution":1.62,
+        "title":"Insulin at pH 2: structural analysis of the conditions promoting insulin fibre formation."},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1ev6",
+        "resolution":1.9,
+        "title":"Structure of the monoclinic form of the M-cresol/insulin R6 hexamer"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1ev6",
+        "resolution":1.9,
+        "title":"Structure of the monoclinic form of the M-cresol/insulin R6 hexamer"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6s4j",
+        "resolution":1.5,
+        "title":"Crystal structure of zinc free A14E, B25H, B29K(N(eps)-[2-(2-[2-(2-[2-(Octadecandioyl-gamma-Glu)amino]ethoxy)ethoxy]acetylamino)ethoxy]ethoxy)acetyl]), desB27, desB30 human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6s4j",
+        "resolution":1.5,
+        "title":"Crystal structure of zinc free A14E, B25H, B29K(N(eps)-[2-(2-[2-(2-[2-(Octadecandioyl-gamma-Glu)amino]ethoxy)ethoxy]acetylamino)ethoxy]ethoxy)acetyl]), desB27, desB30 human insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5co6",
+        "resolution":1.8,
+        "title":"Crystal structure of human zinc insulin at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5co6",
+        "resolution":1.8,
+        "title":"Crystal structure of human zinc insulin at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6p4z",
+        "resolution":1.8,
+        "title":"Structure of gadolinium-caged cobalt (III) insulin hexamer"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6gv0",
+        "resolution":1.26,
+        "title":"Insulin glulisine"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6gnq",
+        "resolution":2.2,
+        "title":"Monoclinic crystalline form of human insulin, complexed with meta-cresol"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5ems",
+        "resolution":2.3,
+        "title":"Crystal Structure of an iodinated insulin analog"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3zu1",
+        "resolution":1.6,
+        "title":"Structure of LysB29(Nepsilon omega-carboxyheptadecanoyl) des(B30) Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3zu1",
+        "resolution":1.6,
+        "title":"Structure of LysB29(Nepsilon omega-carboxyheptadecanoyl) des(B30) Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5co9",
+        "resolution":1.92,
+        "title":"Crystal structure of human zinc insulin at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wby",
+        "resolution":2.6,
+        "title":"Crystal structure of human insulin-degrading enzyme in complex with insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wby",
+        "resolution":2.6,
+        "title":"Crystal structure of human insulin-degrading enzyme in complex with insulin"},
+      {
+        "experimental_method":["Electron Microscopy"],
+        "pdb_id":"6hn5",
+        "resolution":3.2,
+        "title":"Leucine-zippered human insulin receptor ectodomain with single bound insulin - \"upper\" membrane-distal part"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2olz",
+        "resolution":1.7,
+        "title":"Structure of human insulin in presence of thiocyanate at pH 7.0"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2olz",
+        "resolution":1.7,
+        "title":"Structure of human insulin in presence of thiocyanate at pH 7.0"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2omg",
+        "resolution":1.52,
+        "title":"Structure of human insulin cocrystallized with protamine and urea"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2w44",
+        "resolution":2.0,
+        "title":"Structure DeltaA1-A4 insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2ws7",
+        "resolution":2.59,
+        "title":"Semi-synthetic analogue of human insulin ProB26-DTI"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4fg3",
+        "resolution":2.001,
+        "title":"Crystal Structure Analysis of the Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4fg3",
+        "resolution":2.001,
+        "title":"Crystal Structure Analysis of the Human Insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1w8p",
+        "resolution":2.08,
+        "title":"Structural properties of the B25Tyr-NMe-B26Phe insulin mutant."},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1w8p",
+        "resolution":2.08,
+        "title":"Structural properties of the B25Tyr-NMe-B26Phe insulin mutant."},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3p33",
+        "resolution":2.3,
+        "title":"Insulin fibrillation is the Janus face of induced fit. A chiral clamp stabilizes the native state at the expense of activity"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1znj",
+        "resolution":2.0,
+        "title":"INSULIN, MONOCLINIC CRYSTAL FORM"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1znj",
+        "resolution":2.0,
+        "title":"INSULIN, MONOCLINIC CRYSTAL FORM"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1xw7",
+        "resolution":2.3,
+        "title":"Diabetes-Associated Mutations in Human Insulin: Crystal Structure and Photo-Cross-Linking Studies of A-Chain Variant Insulin Wakayama"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1xw7",
+        "resolution":2.3,
+        "title":"Diabetes-Associated Mutations in Human Insulin: Crystal Structure and Photo-Cross-Linking Studies of A-Chain Variant Insulin Wakayama"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1qj0",
+        "resolution":2.4,
+        "title":"HUMAN INSULIN HEXAMERS WITH CHAIN B HIS MUTATED TO TYR"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1rwe",
+        "resolution":1.8,
+        "title":"Enhancing the activity of insulin at receptor edge: crystal structure and photo-cross-linking of A8 analogues"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1rwe",
+        "resolution":1.8,
+        "title":"Enhancing the activity of insulin at receptor edge: crystal structure and photo-cross-linking of A8 analogues"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1evr",
+        "resolution":1.9,
+        "title":"The structure of the resorcinol/insulin R6 hexamer"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1g7b",
+        "resolution":1.3,
+        "title":"1.3 A STRUCTURE OF T3R3 HUMAN INSULIN AT 100 K"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1g7b",
+        "resolution":1.3,
+        "title":"1.3 A STRUCTURE OF T3R3 HUMAN INSULIN AT 100 K"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1ben",
+        "resolution":1.4,
+        "title":"INSULIN COMPLEXED WITH 4-HYDROXYBENZAMIDE"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1lph",
+        "resolution":2.3,
+        "title":"LYS(B28)PRO(B29)-HUMAN INSULIN"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1lph",
+        "resolution":2.3,
+        "title":"LYS(B28)PRO(B29)-HUMAN INSULIN"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2om1",
+        "resolution":1.97,
+        "title":"Structure of human insulin in presence of thiocyanate at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2om0",
+        "resolution":2.05,
+        "title":"Structure of human insulin in presence of urea at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2om0",
+        "resolution":2.05,
+        "title":"Structure of human insulin in presence of urea at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1g7a",
+        "resolution":1.2,
+        "title":"1.2 A structure of T3R3 human insulin at 100 K"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1g7a",
+        "resolution":1.2,
+        "title":"1.2 A structure of T3R3 human insulin at 100 K"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6ck2",
+        "resolution":2.25,
+        "title":"Insulin analog containing a YB26W mutation"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5udp",
+        "resolution":1.348,
+        "title":"High resolution x-ray crystal structure of synthetic insulin lispro"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5udp",
+        "resolution":1.348,
+        "title":"High resolution x-ray crystal structure of synthetic insulin lispro"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5mt3",
+        "resolution":2.02,
+        "title":"Human insulin in complex with serotonin and arginine"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6nwv",
+        "resolution":1.601,
+        "title":"Insulin Lispro Analog"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6gnq",
+        "resolution":2.2,
+        "title":"Monoclinic crystalline form of human insulin, complexed with meta-cresol"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5hrq",
+        "resolution":1.28,
+        "title":"Insulin with proline analog HzP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5hpr",
+        "resolution":1.33,
+        "title":"Insulin with proline analog HyP at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5hpr",
+        "resolution":1.33,
+        "title":"Insulin with proline analog HyP at position B28 in the T2 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5ems",
+        "resolution":2.3,
+        "title":"Crystal Structure of an iodinated insulin analog"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ajx",
+        "resolution":1.2,
+        "title":"Ligand controlled assembly of hexamers, dihexamers, and linear multihexamer structures by an engineered acylated insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ajx",
+        "resolution":1.2,
+        "title":"Ligand controlled assembly of hexamers, dihexamers, and linear multihexamer structures by an engineered acylated insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3v1g",
+        "resolution":2.2,
+        "title":"Forestalling insulin fibrillation by insertion of a chiral clamp mechanism-based application of protein engineering to global health"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3v1g",
+        "resolution":2.2,
+        "title":"Forestalling insulin fibrillation by insertion of a chiral clamp mechanism-based application of protein engineering to global health"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3zqr",
+        "resolution":1.9,
+        "title":"NMePheB25 insulin analogue crystal structure"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3zqr",
+        "resolution":1.9,
+        "title":"NMePheB25 insulin analogue crystal structure"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ajz",
+        "resolution":1.8,
+        "title":"Ligand controlled assembly of hexamers, dihexamers, and linear multihexamer structures by an engineered acylated insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3v19",
+        "resolution":2.0,
+        "title":"Forestalling insulin fibrillation by insertion of a chiral clamp mechanism-based application of protein engineering to global health"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3v19",
+        "resolution":2.0,
+        "title":"Forestalling insulin fibrillation by insertion of a chiral clamp mechanism-based application of protein engineering to global health"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3zs2",
+        "resolution":1.97,
+        "title":"TyrB25,NMePheB26,LysB28,ProB29-insulin analogue crystal structure"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3zs2",
+        "resolution":1.97,
+        "title":"TyrB25,NMePheB26,LysB28,ProB29-insulin analogue crystal structure"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5mt9",
+        "resolution":1.88,
+        "title":"Human insulin in complex with serotonin and arginine"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uru",
+        "resolution":2.41,
+        "title":"Insulin with proline analog DhP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uu3",
+        "resolution":2.25,
+        "title":"Insulin with proline analog DfP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3kq6",
+        "resolution":1.9,
+        "title":"Enhancing the Therapeutic Properties of a Protein by a Designed Zinc-Binding Site, Structural principles of a novel long-acting insulin analog"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3kq6",
+        "resolution":1.9,
+        "title":"Enhancing the Therapeutic Properties of a Protein by a Designed Zinc-Binding Site, Structural principles of a novel long-acting insulin analog"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4akj",
+        "resolution":2.01,
+        "title":"Ligand controlled assembly of hexamers, dihexamers, and linear multihexamer structures by an engineered acylated insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uss",
+        "resolution":2.061,
+        "title":"Insulin with proline analog PiP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uss",
+        "resolution":2.061,
+        "title":"Insulin with proline analog PiP at position B28 in the R6 state"},
+      {
+        "experimental_method":["Electron Microscopy"],
+        "pdb_id":"6hn5",
+        "resolution":3.2,
+        "title":"Leucine-zippered human insulin receptor ectodomain with single bound insulin - \"upper\" membrane-distal part"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wc0",
+        "resolution":2.8,
+        "title":"crystal structure of human insulin degrading enzyme in complex with iodinated insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2wc0",
+        "resolution":2.8,
+        "title":"crystal structure of human insulin degrading enzyme in complex with iodinated insulin"},
+      {
+        "experimental_method":["Electron Microscopy"],
+        "pdb_id":"6sof",
+        "resolution":4.3,
+        "title":"human insulin receptor ectodomain bound by 4 insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6z7y",
+        "resolution":2.2,
+        "title":"Human insulin in complex with the analytical antibody OXI-005 Fab"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6z7y",
+        "resolution":2.2,
+        "title":"Human insulin in complex with the analytical antibody OXI-005 Fab"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2omh",
+        "resolution":1.36,
+        "title":"Structure of human insulin cocrystallized with ARG-12 peptide in presence of urea"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2omh",
+        "resolution":1.36,
+        "title":"Structure of human insulin cocrystallized with ARG-12 peptide in presence of urea"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2omg",
+        "resolution":1.52,
+        "title":"Structure of human insulin cocrystallized with protamine and urea"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3fq9",
+        "resolution":1.35,
+        "title":"Design of an insulin analog with enhanced receptor-binding selectivity. Rationale, structure, and therapeutic implications"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3fq9",
+        "resolution":1.35,
+        "title":"Design of an insulin analog with enhanced receptor-binding selectivity. Rationale, structure, and therapeutic implications"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3jsd",
+        "resolution":2.5,
+        "title":"Insulin's biosynthesis and activity have opposing structural requirements: a new factor in neonatal diabetes mellitus"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3jsd",
+        "resolution":2.5,
+        "title":"Insulin's biosynthesis and activity have opposing structural requirements: a new factor in neonatal diabetes mellitus"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4p65",
+        "resolution":1.5,
+        "title":"Crystal structure of an cyclohexylalanine substituted insulin analog."},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4p65",
+        "resolution":1.5,
+        "title":"Crystal structure of an cyclohexylalanine substituted insulin analog."},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3p33",
+        "resolution":2.3,
+        "title":"Insulin fibrillation is the Janus face of induced fit. A chiral clamp stabilizes the native state at the expense of activity"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3p2x",
+        "resolution":2.0,
+        "title":"Insulin fibrillation is the Janus face of induced fit. A chiaral clamp stabilizes the native state at the expense of activity"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3p2x",
+        "resolution":2.0,
+        "title":"Insulin fibrillation is the Janus face of induced fit. A chiaral clamp stabilizes the native state at the expense of activity"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1qiz",
+        "resolution":2.0,
+        "title":"HUMAN INSULIN HEXAMERS WITH CHAIN B HIS MUTATED TO TYR COMPLEXED WITH RESORCINOL"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1qiz",
+        "resolution":2.0,
+        "title":"HUMAN INSULIN HEXAMERS WITH CHAIN B HIS MUTATED TO TYR COMPLEXED WITH RESORCINOL"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1ben",
+        "resolution":1.4,
+        "title":"INSULIN COMPLEXED WITH 4-HYDROXYBENZAMIDE"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2oly",
+        "resolution":1.7,
+        "title":"Structure of human insulin in presence of urea at pH 7.0"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2om1",
+        "resolution":1.97,
+        "title":"Structure of human insulin in presence of thiocyanate at pH 6.5"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6ck2",
+        "resolution":2.25,
+        "title":"Insulin analog containing a YB26W mutation"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6tyh",
+        "resolution":1.600019,
+        "title":"Four-Disulfide Insulin Analog A22/B22"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6tyh",
+        "resolution":1.600019,
+        "title":"Four-Disulfide Insulin Analog A22/B22"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5mt3",
+        "resolution":2.02,
+        "title":"Human insulin in complex with serotonin and arginine"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6nwv",
+        "resolution":1.601,
+        "title":"Insulin Lispro Analog"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5hpu",
+        "resolution":2.2,
+        "title":"Insulin with proline analog HyP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5hpu",
+        "resolution":2.2,
+        "title":"Insulin with proline analog HyP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5hrq",
+        "resolution":1.28,
+        "title":"Insulin with proline analog HzP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4ajz",
+        "resolution":1.8,
+        "title":"Ligand controlled assembly of hexamers, dihexamers, and linear multihexamer structures by an engineered acylated insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5mt9",
+        "resolution":1.88,
+        "title":"Human insulin in complex with serotonin and arginine"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uru",
+        "resolution":2.41,
+        "title":"Insulin with proline analog DhP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uu3",
+        "resolution":2.25,
+        "title":"Insulin with proline analog DfP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uu4",
+        "resolution":1.973,
+        "title":"Insulin with proline analog ThioP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uu4",
+        "resolution":1.973,
+        "title":"Insulin with proline analog ThioP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5bqq",
+        "resolution":1.54,
+        "title":"Human insulin with intra-chain chemical crosslink between modified B27 and B30"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5mam",
+        "resolution":2.2,
+        "title":"Human insulin in complex with serotonin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5mam",
+        "resolution":2.2,
+        "title":"Human insulin in complex with serotonin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4akj",
+        "resolution":2.01,
+        "title":"Ligand controlled assembly of hexamers, dihexamers, and linear multihexamer structures by an engineered acylated insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1q4v",
+        "resolution":2.0,
+        "title":"CRYSTAL STRUCTURE OF ALLO-ILEA2-INSULIN, AN INACTIVE CHIRAL ANALOGUE: IMPLICATIONS FOR THE MECHANISM OF RECEPTOR"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4xc4",
+        "resolution":1.499,
+        "title":"Insulin co-crystallizes in the presence of it beta-cell chaperone sulfatide"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"4xc4",
+        "resolution":1.499,
+        "title":"Insulin co-crystallizes in the presence of it beta-cell chaperone sulfatide"},
+      {
+        "experimental_method":["Electron Microscopy"],
+        "pdb_id":"6sof",
+        "resolution":4.3,
+        "title":"human insulin receptor ectodomain bound by 4 insulin"},
+      {
+        "experimental_method":["Electron Microscopy"],
+        "pdb_id":"6sof",
+        "resolution":4.3,
+        "title":"human insulin receptor ectodomain bound by 4 insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6z7y",
+        "resolution":2.2,
+        "title":"Human insulin in complex with the analytical antibody OXI-005 Fab"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6z7y",
+        "resolution":2.2,
+        "title":"Human insulin in complex with the analytical antibody OXI-005 Fab"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1zeh",
+        "resolution":1.5,
+        "title":"STRUCTURE OF INSULIN"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1zeh",
+        "resolution":1.5,
+        "title":"STRUCTURE OF INSULIN"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1qiy",
+        "resolution":2.3,
+        "title":"HUMAN INSULIN HEXAMERS WITH CHAIN B HIS MUTATED TO TYR COMPLEXED WITH PHENOL"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1qiy",
+        "resolution":2.3,
+        "title":"HUMAN INSULIN HEXAMERS WITH CHAIN B HIS MUTATED TO TYR COMPLEXED WITH PHENOL"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"2oly",
+        "resolution":1.7,
+        "title":"Structure of human insulin in presence of urea at pH 7.0"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3rov",
+        "resolution":2.3,
+        "title":"Insulin's biosynthesis and activity have opposing structural requirements: a new factor in neonatal diabetes mellitus"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"3rov",
+        "resolution":2.3,
+        "title":"Insulin's biosynthesis and activity have opposing structural requirements: a new factor in neonatal diabetes mellitus"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5bqq",
+        "resolution":1.54,
+        "title":"Human insulin with intra-chain chemical crosslink between modified B27 and B30"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1q4v",
+        "resolution":2.0,
+        "title":"CRYSTAL STRUCTURE OF ALLO-ILEA2-INSULIN, AN INACTIVE CHIRAL ANALOGUE: IMPLICATIONS FOR THE MECHANISM OF RECEPTOR"},
+      {
+        "experimental_method":["Electron Microscopy"],
+        "pdb_id":"6sof",
+        "resolution":4.3,
+        "title":"human insulin receptor ectodomain bound by 4 insulin"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6z7w",
+        "resolution":2.42,
+        "title":"Human insulin in complex with the analytical antibody HUI-018 Fab"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6z7w",
+        "resolution":2.42,
+        "title":"Human insulin in complex with the analytical antibody HUI-018 Fab"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1zeg",
+        "resolution":1.6,
+        "title":"STRUCTURE OF B28 ASP INSULIN IN COMPLEX WITH PHENOL"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"5uqa",
+        "resolution":1.3100024,
+        "title":"Insulin with proline analog FzP at position B28 in the R6 state"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6z7w",
+        "resolution":2.42,
+        "title":"Human insulin in complex with the analytical antibody HUI-018 Fab"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"6z7w",
+        "resolution":2.42,
+        "title":"Human insulin in complex with the analytical antibody HUI-018 Fab"},
+      {
+        "experimental_method":["X-ray diffraction"],
+        "pdb_id":"1xda",
+        "resolution":1.8,
+        "title":"STRUCTURE OF INSULIN"}]
+  }}
diff --git a/test/jalview/fts/threedbeacons/p01308_tdb_resp.txt b/test/jalview/fts/threedbeacons/p01308_tdb_resp.txt
new file mode 100644 (file)
index 0000000..12f40ca
--- /dev/null
@@ -0,0 +1 @@
+{"uniprot_entry":{"sequence_length":110,"ac":"P01308","id":"INS_HUMAN"},"structures":[{"model_identifier":"3w7y","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-03-11","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":0.92,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w7y_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w7y"},{"model_identifier":"3w7y","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-03-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":0.92,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w7y_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w7y"},{"model_identifier":"5e7w","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-10-13","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":0.9519,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5e7w_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5e7w"},{"model_identifier":"5e7w","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-10-13","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":0.9519,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5e7w_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5e7w"},{"model_identifier":"5hqi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-01-21","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":0.97,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5hqi_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5hqi"},{"model_identifier":"5hqi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-01-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":0.97,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5hqi_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5hqi"},{"model_identifier":"1mso","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2002-09-19","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.0,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1mso_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1mso"},{"model_identifier":"1mso","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2002-09-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.0,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1mso_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1mso"},{"model_identifier":"3hyd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-06-22","sequence_identity":100.0,"uniprot_start":35,"uniprot_end":41,"resolution":1.0,"coverage":8.14,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3hyd_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3hyd"},{"model_identifier":"6ver","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-01-02","sequence_identity":91.0,"uniprot_start":25,"uniprot_end":46,"resolution":1.047,"coverage":25.58,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6ver_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6ver"},{"model_identifier":"6ver","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-01-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.047,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6ver_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6ver"},{"model_identifier":"4fka","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-06-13","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.08,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4fka_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4fka"},{"model_identifier":"4fka","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-06-13","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.08,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4fka_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4fka"},{"model_identifier":"3tt8","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-09-14","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.12,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3tt8_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3tt8"},{"model_identifier":"3tt8","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-09-14","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.12,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3tt8_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3tt8"},{"model_identifier":"3w7z","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-03-11","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.15,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w7z_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w7z"},{"model_identifier":"3w7z","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-03-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.15,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w7z_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w7z"},{"model_identifier":"5usp","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-13","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.174,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5usp_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5usp"},{"model_identifier":"5usp","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-13","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.174,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5usp_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5usp"},{"model_identifier":"5uoz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-01","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.17463871031,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uoz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uoz"},{"model_identifier":"5uoz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.17463871031,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uoz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uoz"},{"model_identifier":"5urt","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-12","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.18,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5urt_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5urt"},{"model_identifier":"5urt","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-12","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.18,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5urt_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5urt"},{"model_identifier":"1g7a","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-11-09","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.2,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1g7a_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1g7a"},{"model_identifier":"4ajx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-02-20","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.2,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ajx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ajx"},{"model_identifier":"1g7a","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-11-09","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.2,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1g7a_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1g7a"},{"model_identifier":"4ajx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-02-20","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.2,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ajx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ajx"},{"model_identifier":"5uu2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-15","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.223,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uu2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uu2"},{"model_identifier":"5uu2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-15","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.223,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uu2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uu2"},{"model_identifier":"6gv0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-06-20","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.26,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6gv0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6gv0"},{"model_identifier":"6gv0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-06-20","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.26,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6gv0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6gv0"},{"model_identifier":"5hrq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-01-24","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.28,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5hrq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5hrq"},{"model_identifier":"5hrq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-01-24","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.28,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5hrq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5hrq"},{"model_identifier":"1g7b","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-11-09","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1g7b_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1g7b"},{"model_identifier":"5usv","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-14","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5usv_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5usv"},{"model_identifier":"3bxq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-01-14","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3bxq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3bxq"},{"model_identifier":"1g7b","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-11-09","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1g7b_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1g7b"},{"model_identifier":"5usv","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-14","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5usv_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5usv"},{"model_identifier":"3bxq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-01-14","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3bxq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3bxq"},{"model_identifier":"5uqa","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-07","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.31000248093,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uqa_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uqa"},{"model_identifier":"5uqa","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-07","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.31000248093,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uqa_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uqa"},{"model_identifier":"5hpr","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-01-21","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.33,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5hpr_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5hpr"},{"model_identifier":"5hpr","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-01-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.33,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5hpr_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5hpr"},{"model_identifier":"5udp","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-12-28","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.348,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5udp_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5udp"},{"model_identifier":"5udp","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-12-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.348,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5udp_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5udp"},{"model_identifier":"3fq9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-01-07","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.35,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3fq9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3fq9"},{"model_identifier":"5ena","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-11-09","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.35,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5ena_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5ena"},{"model_identifier":"3exx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-10-17","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.35,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3exx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3exx"},{"model_identifier":"6s34","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-06-24","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.35,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6s34_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6s34"},{"model_identifier":"5ena","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-11-09","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.35,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5ena_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5ena"},{"model_identifier":"3exx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-10-17","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.35,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3exx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3exx"},{"model_identifier":"6s34","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-06-24","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.35,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6s34_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6s34"},{"model_identifier":"3fq9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-01-07","sequence_identity":95.0,"uniprot_start":91,"uniprot_end":110,"resolution":1.35,"coverage":23.26,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3fq9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3fq9"},{"model_identifier":"6tc2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-11-04","sequence_identity":100.0,"uniprot_start":1,"uniprot_end":110,"resolution":1.36,"coverage":100.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6tc2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6tc2"},{"model_identifier":"2omh","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-22","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.36,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2omh_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2omh"},{"model_identifier":"2omh","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-22","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.36,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2omh_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2omh"},{"model_identifier":"4nib","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-11-05","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.4,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4nib_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4nib"},{"model_identifier":"4cy7","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-04-10","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.4,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4cy7_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4cy7"},{"model_identifier":"3w80","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-03-11","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.4,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w80_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w80"},{"model_identifier":"1ben","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-02-15","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.4,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1ben_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1ben"},{"model_identifier":"7nhu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2021-02-11","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.4,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/7nhu_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/7nhu"},{"model_identifier":"4nib","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-11-05","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.4,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4nib_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4nib"},{"model_identifier":"4cy7","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-04-10","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.4,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4cy7_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4cy7"},{"model_identifier":"7nhu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2021-02-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.4,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/7nhu_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/7nhu"},{"model_identifier":"3w80","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-03-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.4,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w80_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w80"},{"model_identifier":"1ben","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-02-15","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.4,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1ben_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1ben"},{"model_identifier":"6vet","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-01-02","sequence_identity":91.0,"uniprot_start":25,"uniprot_end":46,"resolution":1.46,"coverage":25.58,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6vet_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6vet"},{"model_identifier":"6vet","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-01-02","sequence_identity":90.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.46,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6vet_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6vet"},{"model_identifier":"4ey1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.471,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ey1_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ey1"},{"model_identifier":"4ey9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.471,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ey9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ey9"},{"model_identifier":"4eyd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.471,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4eyd_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4eyd"},{"model_identifier":"4ey1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.471,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ey1_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ey1"},{"model_identifier":"4ey9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.471,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ey9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ey9"},{"model_identifier":"4eyd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.471,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4eyd_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4eyd"},{"model_identifier":"4xc4","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-12-17","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.499,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4xc4_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4xc4"},{"model_identifier":"4xc4","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-12-17","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.499,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4xc4_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4xc4"},{"model_identifier":"1zeh","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-05-01","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1zeh_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1zeh"},{"model_identifier":"2wrx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-02","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wrx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wrx"},{"model_identifier":"2ws6","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws6_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws6"},{"model_identifier":"2ws6","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws6_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws6"},{"model_identifier":"4cxl","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-04-07","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4cxl_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4cxl"},{"model_identifier":"5en9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-11-09","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5en9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5en9"},{"model_identifier":"4p65","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-03-21","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4p65_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4p65"},{"model_identifier":"2c8r","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2005-12-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.5,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2c8r_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2c8r"},{"model_identifier":"6s4j","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-06-28","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.5,"coverage":32.56,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6s4j_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6s4j"},{"model_identifier":"1zeh","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-05-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1zeh_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1zeh"},{"model_identifier":"2wrx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wrx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wrx"},{"model_identifier":"2ws6","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws6_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws6"},{"model_identifier":"2c8r","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2005-12-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2c8r_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2c8r"},{"model_identifier":"4cxl","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-04-07","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4cxl_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4cxl"},{"model_identifier":"5en9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-11-09","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5en9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5en9"},{"model_identifier":"4p65","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-03-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4p65_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4p65"},{"model_identifier":"6s4j","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-06-28","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6s4j_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6s4j"},{"model_identifier":"6s4i","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-06-28","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.511,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6s4i_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6s4i"},{"model_identifier":"6s4i","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-06-28","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.511,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6s4i_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6s4i"},{"model_identifier":"2omg","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-22","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.52,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2omg_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2omg"},{"model_identifier":"2omg","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-22","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.52,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2omg_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2omg"},{"model_identifier":"4eyn","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.532,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4eyn_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4eyn"},{"model_identifier":"4eyn","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.532,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4eyn_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4eyn"},{"model_identifier":"5bqq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-29","sequence_identity":96.0,"uniprot_start":25,"uniprot_end":52,"resolution":1.54,"coverage":32.56,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5bqq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5bqq"},{"model_identifier":"5bqq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.54,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5bqq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5bqq"},{"model_identifier":"4exx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.55,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4exx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4exx"},{"model_identifier":"5t7r","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-09-05","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.55,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5t7r_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5t7r"},{"model_identifier":"4exx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.55,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4exx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4exx"},{"model_identifier":"5t7r","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-09-05","sequence_identity":90.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.55,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5t7r_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5t7r"},{"model_identifier":"2wru","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-02","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":49,"resolution":1.57,"coverage":29.07,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wru_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wru"},{"model_identifier":"2wru","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.57,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wru_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wru"},{"model_identifier":"6o17","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-02-18","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.58,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6o17_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6o17"},{"model_identifier":"6o17","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-02-18","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.58,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6o17_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6o17"},{"model_identifier":"4une","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-05-28","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.59,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4une_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4une"},{"model_identifier":"4une","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-05-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.59,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4une_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4une"},{"model_identifier":"4eyp","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.591,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4eyp_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4eyp"},{"model_identifier":"4f1b","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.591,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1b_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1b"},{"model_identifier":"4eyp","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.591,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4eyp_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4eyp"},{"model_identifier":"4f1b","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.591,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1b_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1b"},{"model_identifier":"1zeg","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-05-01","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.6,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1zeg_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1zeg"},{"model_identifier":"2ws1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.6,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws1_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws1"},{"model_identifier":"3zu1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-07-13","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.6,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3zu1_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3zu1"},{"model_identifier":"1trz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1993-11-19","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.6,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1trz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1trz"},{"model_identifier":"4iuz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-01-22","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.6,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4iuz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4iuz"},{"model_identifier":"3i3z","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-07-01","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.6,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3i3z_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3i3z"},{"model_identifier":"1uz9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-03-08","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.6,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1uz9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1uz9"},{"model_identifier":"3e7y","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-08-19","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.6,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3e7y_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3e7y"},{"model_identifier":"3i3z","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-07-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.6,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3i3z_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3i3z"},{"model_identifier":"1zeg","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-05-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.6,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1zeg_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1zeg"},{"model_identifier":"2ws1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.6,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws1_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws1"},{"model_identifier":"1uz9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-03-08","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.6,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1uz9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1uz9"},{"model_identifier":"3zu1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-07-13","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.6,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3zu1_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3zu1"},{"model_identifier":"3e7y","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-08-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.6,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3e7y_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3e7y"},{"model_identifier":"1trz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1993-11-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.6,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1trz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1trz"},{"model_identifier":"4iuz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-01-22","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.6,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4iuz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4iuz"},{"model_identifier":"6tyh","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-08-08","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.60001899481,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6tyh_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6tyh"},{"model_identifier":"6tyh","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-08-08","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.60001899481,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6tyh_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6tyh"},{"model_identifier":"6nwv","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-02-07","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.601,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6nwv_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6nwv"},{"model_identifier":"6nwv","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-02-07","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.601,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6nwv_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6nwv"},{"model_identifier":"1guj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2002-01-28","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.62,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1guj_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1guj"},{"model_identifier":"1guj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2002-01-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.62,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1guj_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1guj"},{"model_identifier":"4f1d","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.637,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1d_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1d"},{"model_identifier":"4f1g","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.637,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1g_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1g"},{"model_identifier":"4f4t","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-11","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.637,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f4t_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f4t"},{"model_identifier":"4f4v","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-11","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.637,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f4v_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f4v"},{"model_identifier":"4f51","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-11","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.637,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f51_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f51"},{"model_identifier":"4f1d","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.637,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1d_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1d"},{"model_identifier":"4f1g","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.637,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1g_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1g"},{"model_identifier":"4f4t","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.637,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f4t_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f4t"},{"model_identifier":"4f4v","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.637,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f4v_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f4v"},{"model_identifier":"4f51","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.637,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f51_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f51"},{"model_identifier":"4ex1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-04-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.657,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ex1_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ex1"},{"model_identifier":"4ex1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-04-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.657,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ex1_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ex1"},{"model_identifier":"4iyd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-01-28","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.66,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4iyd_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4iyd"},{"model_identifier":"4iyd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-01-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":109,"resolution":1.66,"coverage":23.26,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4iyd_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4iyd"},{"model_identifier":"3utq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-11-26","sequence_identity":100.0,"uniprot_start":15,"uniprot_end":24,"resolution":1.67,"coverage":0.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3utq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3utq"},{"model_identifier":"4f0o","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-04","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.672,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f0o_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f0o"},{"model_identifier":"4f0o","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-04","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.672,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f0o_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f0o"},{"model_identifier":"4f8f","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-17","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.676,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f8f_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f8f"},{"model_identifier":"4f8f","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-17","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.676,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f8f_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f8f"},{"model_identifier":"4f0n","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-04","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.679,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f0n_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f0n"},{"model_identifier":"4f0n","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-04","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.679,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f0n_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f0n"},{"model_identifier":"5c0d","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-06-12","sequence_identity":90.0,"uniprot_start":15,"uniprot_end":24,"resolution":1.68,"coverage":0.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5c0d_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5c0d"},{"model_identifier":"4f1f","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.684,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1f_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1f"},{"model_identifier":"4f1f","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.684,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1f_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1f"},{"model_identifier":"3zi3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-01-02","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.7,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3zi3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3zi3"},{"model_identifier":"4f1c","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.7,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1c_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1c"},{"model_identifier":"4cxn","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-04-07","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.7,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4cxn_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4cxn"},{"model_identifier":"5cny","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-07-18","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.7,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5cny_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5cny"},{"model_identifier":"5co2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-07-19","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.7,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5co2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5co2"},{"model_identifier":"2oly","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-20","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.7,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2oly_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2oly"},{"model_identifier":"2olz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-20","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.7,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2olz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2olz"},{"model_identifier":"5boq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-27","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.7,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5boq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5boq"},{"model_identifier":"5viz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-04-17","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.7,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5viz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5viz"},{"model_identifier":"3e7z","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-08-19","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.7,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3e7z_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3e7z"},{"model_identifier":"3zi3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-01-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.7,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3zi3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3zi3"},{"model_identifier":"4f1c","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.7,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1c_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1c"},{"model_identifier":"4cxn","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-04-07","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.7,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4cxn_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4cxn"},{"model_identifier":"5cny","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-07-18","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.7,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5cny_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5cny"},{"model_identifier":"3e7z","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-08-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.7,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3e7z_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3e7z"},{"model_identifier":"5co2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-07-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.7,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5co2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5co2"},{"model_identifier":"2oly","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-20","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.7,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2oly_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2oly"},{"model_identifier":"2olz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-20","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.7,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2olz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2olz"},{"model_identifier":"5boq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-27","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.7,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5boq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5boq"},{"model_identifier":"5viz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-04-17","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":109,"resolution":1.7,"coverage":23.26,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5viz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5viz"},{"model_identifier":"4rxw","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-12-12","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.73,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4rxw_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4rxw"},{"model_identifier":"4rxw","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-12-12","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.73,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4rxw_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4rxw"},{"model_identifier":"5bts","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-06-03","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.77,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5bts_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5bts"},{"model_identifier":"5bts","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-06-03","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.77,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5bts_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5bts"},{"model_identifier":"4gbc","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-07-27","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.778,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4gbc_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4gbc"},{"model_identifier":"4gbc","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-07-27","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.778,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4gbc_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4gbc"},{"model_identifier":"1ev3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-04-19","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.78,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1ev3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1ev3"},{"model_identifier":"1ev3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-04-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.78,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1ev3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1ev3"},{"model_identifier":"4ewz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-04-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.791,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ewz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ewz"},{"model_identifier":"4ewz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-04-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.791,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ewz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ewz"},{"model_identifier":"4f1a","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.8,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1a_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1a"},{"model_identifier":"2vjz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-12-14","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.8,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2vjz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2vjz"},{"model_identifier":"1rwe","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2003-12-16","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.8,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1rwe_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1rwe"},{"model_identifier":"5co6","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-07-19","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.8,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5co6_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5co6"},{"model_identifier":"6p4z","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-05-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.8,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6p4z_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6p4z"},{"model_identifier":"1xda","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-12-18","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.8,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1xda_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1xda"},{"model_identifier":"4ajz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-02-21","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.8,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ajz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ajz"},{"model_identifier":"4iyf","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-01-28","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.8,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4iyf_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4iyf"},{"model_identifier":"2ceu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-02-10","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":49,"resolution":1.8,"coverage":29.07,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ceu_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ceu"},{"model_identifier":"4f1a","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-05-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.8,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4f1a_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4f1a"},{"model_identifier":"2ceu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-02-10","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.8,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ceu_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ceu"},{"model_identifier":"2vjz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-12-14","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.8,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2vjz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2vjz"},{"model_identifier":"1rwe","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2003-12-16","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.8,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1rwe_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1rwe"},{"model_identifier":"1xda","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-12-18","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.8,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1xda_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1xda"},{"model_identifier":"5co6","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-07-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.8,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5co6_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5co6"},{"model_identifier":"4ajz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-02-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.8,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ajz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ajz"},{"model_identifier":"6p4z","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-05-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.8,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6p4z_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6p4z"},{"model_identifier":"4iyf","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2013-01-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":109,"resolution":1.8,"coverage":23.26,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4iyf_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4iyf"},{"model_identifier":"4ung","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-05-28","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.81,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ung_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ung"},{"model_identifier":"4ung","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-05-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.81,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ung_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ung"},{"model_identifier":"6h3m","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-07-19","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.821,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6h3m_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6h3m"},{"model_identifier":"6h3m","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-07-19","sequence_identity":90.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.821,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6h3m_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6h3m"},{"model_identifier":"3inc","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-08-12","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.85,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3inc_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3inc"},{"model_identifier":"3i40","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-07-01","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.85,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3i40_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3i40"},{"model_identifier":"6ves","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-01-02","sequence_identity":95.0,"uniprot_start":25,"uniprot_end":46,"resolution":1.85,"coverage":25.58,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6ves_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6ves"},{"model_identifier":"3i40","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-07-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.85,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3i40_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3i40"},{"model_identifier":"6ves","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-01-02","sequence_identity":90.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.85,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6ves_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6ves"},{"model_identifier":"3inc","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-08-12","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.85,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3inc_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3inc"},{"model_identifier":"4z77","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-04-06","sequence_identity":89.0,"uniprot_start":39,"uniprot_end":47,"resolution":1.85,"coverage":10.47,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4z77_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4z77"},{"model_identifier":"4ex0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-04-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.86,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ex0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ex0"},{"model_identifier":"4ex0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-04-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.86,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ex0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ex0"},{"model_identifier":"4gbn","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-07-27","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.872,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4gbn_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4gbn"},{"model_identifier":"4gbn","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-07-27","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.872,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4gbn_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4gbn"},{"model_identifier":"5mt9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-01-07","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.88,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5mt9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5mt9"},{"model_identifier":"5mt9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-01-07","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.88,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5mt9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5mt9"},{"model_identifier":"4z76","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-04-06","sequence_identity":89.0,"uniprot_start":39,"uniprot_end":47,"resolution":1.88,"coverage":10.47,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4z76_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4z76"},{"model_identifier":"3ilg","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-08-07","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.9,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3ilg_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3ilg"},{"model_identifier":"3zqr","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-06-10","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.9,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3zqr_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3zqr"},{"model_identifier":"3kq6","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-11-17","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.9,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3kq6_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3kq6"},{"model_identifier":"1ev6","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-04-19","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.9,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1ev6_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1ev6"},{"model_identifier":"1evr","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-04-20","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.9,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1evr_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1evr"},{"model_identifier":"1tyl","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1994-06-21","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.9,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1tyl_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1tyl"},{"model_identifier":"1tym","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1994-06-21","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.9,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1tym_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1tym"},{"model_identifier":"5bpo","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-28","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.9,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5bpo_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5bpo"},{"model_identifier":"1htv","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-01-01","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":51,"resolution":1.9,"coverage":31.4,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1htv_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1htv"},{"model_identifier":"2ws4","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":49,"resolution":1.9,"coverage":29.07,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws4_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws4"},{"model_identifier":"1htv","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-01-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1htv_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1htv"},{"model_identifier":"2ws4","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws4_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws4"},{"model_identifier":"3ilg","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-08-07","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3ilg_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3ilg"},{"model_identifier":"3zqr","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-06-10","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3zqr_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3zqr"},{"model_identifier":"3kq6","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-11-17","sequence_identity":90.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3kq6_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3kq6"},{"model_identifier":"1ev6","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-04-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1ev6_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1ev6"},{"model_identifier":"1evr","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-04-20","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1evr_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1evr"},{"model_identifier":"1tyl","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1994-06-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1tyl_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1tyl"},{"model_identifier":"1tym","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1994-06-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1tym_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1tym"},{"model_identifier":"5bpo","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5bpo_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5bpo"},{"model_identifier":"5co9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-07-20","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.92,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5co9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5co9"},{"model_identifier":"5co9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-07-20","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.92,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5co9_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5co9"},{"model_identifier":"1os3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2003-03-18","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.95,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1os3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1os3"},{"model_identifier":"2c8q","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2005-12-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.95,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2c8q_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2c8q"},{"model_identifier":"2c8q","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2005-12-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.95,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2c8q_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2c8q"},{"model_identifier":"1os3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2003-03-18","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.95,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1os3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1os3"},{"model_identifier":"2om1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-20","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.97,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2om1_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2om1"},{"model_identifier":"3zs2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-06-21","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.97,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3zs2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3zs2"},{"model_identifier":"2om1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-20","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.97,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2om1_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2om1"},{"model_identifier":"3zs2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-06-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.97,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3zs2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3zs2"},{"model_identifier":"5uu4","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-16","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":1.973,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uu4_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uu4"},{"model_identifier":"5uu4","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-16","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.973,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uu4_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uu4"},{"model_identifier":"3u4n","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-10-10","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":53,"resolution":1.98,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3u4n_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3u4n"},{"model_identifier":"4efx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-03-30","sequence_identity":96.0,"uniprot_start":25,"uniprot_end":52,"resolution":1.98,"coverage":32.56,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4efx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4efx"},{"model_identifier":"3u4n","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-10-10","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.98,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3u4n_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3u4n"},{"model_identifier":"4efx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-03-30","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":1.98,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4efx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4efx"},{"model_identifier":"1q4v","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2003-08-04","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.0,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1q4v_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1q4v"},{"model_identifier":"3p2x","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-10-04","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.0,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3p2x_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3p2x"},{"model_identifier":"2qiu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-07-05","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.0,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2qiu_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2qiu"},{"model_identifier":"3v19","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-12-09","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.0,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3v19_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3v19"},{"model_identifier":"1j73","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-05-15","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.0,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1j73_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1j73"},{"model_identifier":"1qiz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1999-06-18","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.0,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1qiz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1qiz"},{"model_identifier":"1znj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1997-09-23","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.0,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1znj_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1znj"},{"model_identifier":"2r36","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-08-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.0,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2r36_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2r36"},{"model_identifier":"2w44","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-11-21","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":2.0,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2w44_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2w44"},{"model_identifier":"2qiu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-07-05","sequence_identity":100.0,"uniprot_start":89,"uniprot_end":110,"resolution":2.0,"coverage":25.58,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2qiu_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2qiu"},{"model_identifier":"2r36","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-08-29","sequence_identity":100.0,"uniprot_start":89,"uniprot_end":110,"resolution":2.0,"coverage":25.58,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2r36_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2r36"},{"model_identifier":"1q4v","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2003-08-04","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.0,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1q4v_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1q4v"},{"model_identifier":"3p2x","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-10-04","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.0,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3p2x_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3p2x"},{"model_identifier":"3v19","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-12-09","sequence_identity":86.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.0,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3v19_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3v19"},{"model_identifier":"1j73","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-05-15","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.0,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1j73_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1j73"},{"model_identifier":"1qiz","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1999-06-18","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.0,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1qiz_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1qiz"},{"model_identifier":"1znj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1997-09-23","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.0,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1znj_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1znj"},{"model_identifier":"2w44","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-11-21","sequence_identity":100.0,"uniprot_start":94,"uniprot_end":110,"resolution":2.0,"coverage":19.77,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2w44_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2w44"},{"model_identifier":"2omq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-22","sequence_identity":100.0,"uniprot_start":36,"uniprot_end":41,"resolution":2.0,"coverage":6.98,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2omq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2omq"},{"model_identifier":"4fg3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-06-02","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.001,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4fg3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4fg3"},{"model_identifier":"4fg3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-06-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.001,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4fg3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4fg3"},{"model_identifier":"4akj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-02-23","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":2.01,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4akj_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4akj"},{"model_identifier":"4akj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-02-23","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.01,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4akj_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4akj"},{"model_identifier":"5mt3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-01-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.02,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5mt3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5mt3"},{"model_identifier":"5mt3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-01-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.02,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5mt3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5mt3"},{"model_identifier":"2om0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-20","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.05,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2om0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2om0"},{"model_identifier":"3q6e","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-12-31","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.05,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3q6e_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3q6e"},{"model_identifier":"2om0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-20","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.05,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2om0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2om0"},{"model_identifier":"3q6e","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-12-31","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.05,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3q6e_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3q6e"},{"model_identifier":"5uss","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-13","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.061,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uss_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uss"},{"model_identifier":"5uss","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-13","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.061,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uss_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uss"},{"model_identifier":"1w8p","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-09-24","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.08,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1w8p_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1w8p"},{"model_identifier":"2r35","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-08-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.08,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2r35_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2r35"},{"model_identifier":"2r35","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-08-29","sequence_identity":100.0,"uniprot_start":89,"uniprot_end":110,"resolution":2.08,"coverage":25.58,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2r35_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2r35"},{"model_identifier":"1w8p","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-09-24","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.08,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1w8p_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1w8p"},{"model_identifier":"2ws0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.1,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws0"},{"model_identifier":"2ws0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.1,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws0"},{"model_identifier":"2wrv","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-02","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":49,"resolution":2.15,"coverage":29.07,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wrv_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wrv"},{"model_identifier":"2wrv","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.15,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wrv_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wrv"},{"model_identifier":"6z7y","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-06-02","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.2,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6z7y_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6z7y"},{"model_identifier":"5mam","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-11-03","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.2,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5mam_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5mam"},{"model_identifier":"3v1g","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-12-09","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.2,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3v1g_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3v1g"},{"model_identifier":"2vk0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-12-14","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.2,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2vk0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2vk0"},{"model_identifier":"3ir0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-08-21","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.2,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3ir0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3ir0"},{"model_identifier":"5hpu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-01-21","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.2,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5hpu_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5hpu"},{"model_identifier":"2g56","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-02-22","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.2,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2g56_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2g56"},{"model_identifier":"6gnq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-05-31","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.2,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6gnq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6gnq"},{"model_identifier":"6z7y","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-06-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.2,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6z7y_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6z7y"},{"model_identifier":"5mam","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-11-03","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.2,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5mam_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5mam"},{"model_identifier":"3v1g","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-12-09","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.2,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3v1g_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3v1g"},{"model_identifier":"2vk0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-12-14","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.2,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2vk0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2vk0"},{"model_identifier":"3ir0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-08-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.2,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3ir0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3ir0"},{"model_identifier":"5hpu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-01-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.2,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5hpu_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5hpu"},{"model_identifier":"6gnq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-05-31","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.2,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6gnq_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6gnq"},{"model_identifier":"4ewx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-04-28","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.201,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ewx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ewx"},{"model_identifier":"4ewx","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-04-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.201,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ewx_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ewx"},{"model_identifier":"2omi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-22","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.24,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2omi_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2omi"},{"model_identifier":"2omi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-01-22","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.24,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2omi_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2omi"},{"model_identifier":"5uu3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-16","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.25,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uu3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uu3"},{"model_identifier":"1os4","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2003-03-18","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.25,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1os4_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1os4"},{"model_identifier":"2g54","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-02-22","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.25,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2g54_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2g54"},{"model_identifier":"6ck2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-02-27","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.25,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6ck2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6ck2"},{"model_identifier":"2r34","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-08-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.25,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2r34_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2r34"},{"model_identifier":"2r34","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-08-29","sequence_identity":100.0,"uniprot_start":89,"uniprot_end":110,"resolution":2.25,"coverage":25.58,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2r34_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2r34"},{"model_identifier":"5uu3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-16","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.25,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uu3_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uu3"},{"model_identifier":"1os4","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2003-03-18","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.25,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1os4_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1os4"},{"model_identifier":"6ck2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-02-27","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.25,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6ck2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6ck2"},{"model_identifier":"4ak0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-02-21","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":53,"resolution":2.28,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ak0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ak0"},{"model_identifier":"4ak0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-02-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.28,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4ak0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4ak0"},{"model_identifier":"3p33","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-10-04","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3p33_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3p33"},{"model_identifier":"5ems","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-11-06","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5ems_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5ems"},{"model_identifier":"1qiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1999-06-18","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1qiy_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1qiy"},{"model_identifier":"1lph","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1995-04-19","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1lph_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1lph"},{"model_identifier":"3rov","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-04-26","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3rov_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3rov"},{"model_identifier":"4eww","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-04-28","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4eww_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4eww"},{"model_identifier":"1xw7","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-10-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1xw7_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1xw7"},{"model_identifier":"3p33","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-10-04","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3p33_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3p33"},{"model_identifier":"5ems","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-11-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5ems_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5ems"},{"model_identifier":"1qiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1999-06-18","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1qiy_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1qiy"},{"model_identifier":"1lph","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1995-04-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1lph_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1lph"},{"model_identifier":"3rov","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-04-26","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3rov_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3rov"},{"model_identifier":"4eww","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-04-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4eww_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4eww"},{"model_identifier":"1xw7","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-10-29","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1xw7_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1xw7"},{"model_identifier":"4z78","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-04-06","sequence_identity":100.0,"uniprot_start":39,"uniprot_end":48,"resolution":2.304,"coverage":11.63,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4z78_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4z78"},{"model_identifier":"4wdi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-09-08","sequence_identity":100.0,"uniprot_start":39,"uniprot_end":47,"resolution":2.313,"coverage":10.47,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4wdi_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4wdi"},{"model_identifier":"1qj0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1999-06-18","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.4,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1qj0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1qj0"},{"model_identifier":"4gbk","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-07-27","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.4,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4gbk_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4gbk"},{"model_identifier":"1qj0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1999-06-18","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.4,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1qj0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1qj0"},{"model_identifier":"4gbk","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-07-27","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.4,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4gbk_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4gbk"},{"model_identifier":"1jk8","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-07-11","sequence_identity":100.0,"uniprot_start":35,"uniprot_end":47,"resolution":2.4,"coverage":15.12,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1jk8_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1jk8"},{"model_identifier":"5uru","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-13","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.41,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uru_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uru"},{"model_identifier":"2wrw","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-02","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":49,"resolution":2.41,"coverage":29.07,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wrw_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wrw"},{"model_identifier":"2wrw","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.41,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wrw_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wrw"},{"model_identifier":"5uru","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-02-13","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.41,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5uru_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5uru"},{"model_identifier":"6z7w","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-06-02","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.42,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6z7w_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6z7w"},{"model_identifier":"6z7w","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-06-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.42,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6z7w_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6z7w"},{"model_identifier":"1jca","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-06-08","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1jca_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1jca"},{"model_identifier":"3jsd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-10","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3jsd_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3jsd"},{"model_identifier":"1b9e","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-11-12","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1b9e_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1b9e"},{"model_identifier":"4gbl","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-07-27","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4gbl_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4gbl"},{"model_identifier":"1jca","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-06-08","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1jca_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1jca"},{"model_identifier":"3jsd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-10","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3jsd_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3jsd"},{"model_identifier":"1b9e","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-11-12","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1b9e_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1b9e"},{"model_identifier":"4gbl","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-07-27","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4gbl_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4gbl"},{"model_identifier":"4y19","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-02-07","sequence_identity":100.0,"uniprot_start":75,"uniprot_end":90,"resolution":2.5,"coverage":18.6,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4y19_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4y19"},{"model_identifier":"4gbi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-07-27","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.502,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4gbi_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4gbi"},{"model_identifier":"4gbi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-07-27","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.502,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4gbi_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4gbi"},{"model_identifier":"2ws7","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":96.0,"uniprot_start":25,"uniprot_end":50,"resolution":2.59,"coverage":30.23,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws7_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws7"},{"model_identifier":"2ws7","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-09-03","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.59,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2ws7_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2ws7"},{"model_identifier":"2wby","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-03-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":109,"resolution":2.6,"coverage":23.26,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wby_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wby"},{"model_identifier":"2wby","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-03-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":43,"resolution":2.6,"coverage":22.09,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wby_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wby"},{"model_identifier":"3utt","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-11-26","sequence_identity":100.0,"uniprot_start":15,"uniprot_end":24,"resolution":2.6,"coverage":0.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3utt_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3utt"},{"model_identifier":"3uts","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-11-26","sequence_identity":100.0,"uniprot_start":15,"uniprot_end":24,"resolution":2.712,"coverage":0.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3uts_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3uts"},{"model_identifier":"4unh","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-05-28","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.75,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4unh_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4unh"},{"model_identifier":"4unh","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-05-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.75,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4unh_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4unh"},{"model_identifier":"2wc0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-03-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.8,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wc0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wc0"},{"model_identifier":"2wc0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-03-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.8,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2wc0_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2wc0"},{"model_identifier":"5wdm","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-07-05","sequence_identity":57.0,"uniprot_start":25,"uniprot_end":110,"resolution":2.803,"coverage":66.28,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5wdm_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5wdm"},{"model_identifier":"6vep","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-01-02","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":2.9,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6vep_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6vep"},{"model_identifier":"6vep","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-01-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":2.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6vep_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6vep"},{"model_identifier":"5hyj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-02-01","sequence_identity":90.0,"uniprot_start":15,"uniprot_end":24,"resolution":3.06,"coverage":0.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5hyj_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5hyj"},{"model_identifier":"6hn5","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-09-14","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":3.2,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6hn5_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6hn5"},{"model_identifier":"6hn5","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-09-14","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":3.2,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6hn5_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6hn5"},{"model_identifier":"5cjo","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-07-14","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":109,"resolution":3.287,"coverage":23.26,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5cjo_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5cjo"},{"model_identifier":"4oga","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-01-15","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":3.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4oga_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4oga"},{"model_identifier":"4oga","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-01-15","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":3.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4oga_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4oga"},{"model_identifier":"6b70","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-10-03","sequence_identity":100.0,"uniprot_start":1,"uniprot_end":110,"resolution":3.7,"coverage":100.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6b70_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6b70"},{"model_identifier":"6b3q","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-09-22","sequence_identity":100.0,"uniprot_start":1,"uniprot_end":110,"resolution":3.7,"coverage":100.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6b3q_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6b3q"},{"model_identifier":"6bfc","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-10-26","sequence_identity":100.0,"uniprot_start":1,"uniprot_end":110,"resolution":3.7,"coverage":100.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6bfc_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6bfc"},{"model_identifier":"7bw8","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-04-14","sequence_identity":86.0,"uniprot_start":25,"uniprot_end":110,"resolution":3.8,"coverage":86.05,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/7bw8_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/7bw8"},{"model_identifier":"3w11","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-11-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":3.9,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w11_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w11"},{"model_identifier":"3w11","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-11-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":3.9,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w11_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w11"},{"model_identifier":"5wob","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-08-01","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":109,"resolution":3.95,"coverage":23.26,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5wob_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5wob"},{"model_identifier":"4y1a","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-02-07","sequence_identity":100.0,"uniprot_start":75,"uniprot_end":90,"resolution":4.0,"coverage":18.6,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4y1a_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4y1a"},{"model_identifier":"7bw7","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-04-13","sequence_identity":86.0,"uniprot_start":25,"uniprot_end":110,"resolution":4.1,"coverage":86.05,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/7bw7_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/7bw7"},{"model_identifier":"6sof","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-08-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":4.3,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6sof_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6sof"},{"model_identifier":"6ce9","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-02-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":4.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6ce9_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6ce9"},{"model_identifier":"6sof","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-08-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":4.3,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6sof_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6sof"},{"model_identifier":"3w12","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-11-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":49,"resolution":4.301,"coverage":29.07,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w12_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w12"},{"model_identifier":"3w12","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-11-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":4.301,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w12_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w12"},{"model_identifier":"3w13","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-11-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":49,"resolution":4.303,"coverage":29.07,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w13_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w13"},{"model_identifier":"3w13","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-11-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":4.303,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3w13_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3w13"},{"model_identifier":"6jk8","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-02-27","sequence_identity":100.0,"uniprot_start":1,"uniprot_end":110,"resolution":4.7,"coverage":100.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6jk8_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6jk8"},{"model_identifier":"6ceb","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-02-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":4.7,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6ceb_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6ceb"},{"model_identifier":"7bwa","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-04-14","sequence_identity":86.0,"uniprot_start":25,"uniprot_end":110,"resolution":4.9,"coverage":86.05,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/7bwa_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/7bwa"},{"model_identifier":"6ce7","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2018-02-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":7.4,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6ce7_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6ce7"},{"model_identifier":"6jr3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-04-02","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":14.5,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6jr3_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6jr3"},{"model_identifier":"6jr3","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-04-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":14.5,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6jr3_updated.cif","model_format":"MMCIF","experimental_method":"ELECTRON MICROSCOPY","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6jr3"},{"model_identifier":"2kqp","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-11-12","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":110,"resolution":null,"coverage":100.0,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2kqp_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2kqp"},{"model_identifier":"1efe","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-02-08","sequence_identity":65.0,"uniprot_start":25,"uniprot_end":110,"resolution":null,"coverage":69.77,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1efe_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1efe"},{"model_identifier":"6u46","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-08-23","sequence_identity":61.0,"uniprot_start":25,"uniprot_end":109,"resolution":null,"coverage":67.44,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6u46_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6u46"},{"model_identifier":"1sju","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1997-10-09","sequence_identity":56.0,"uniprot_start":25,"uniprot_end":110,"resolution":null,"coverage":58.14,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1sju_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1sju"},{"model_identifier":"5mwq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-01-19","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":56,"resolution":null,"coverage":37.21,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5mwq_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5mwq"},{"model_identifier":"6k59","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-05-28","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":56,"resolution":null,"coverage":37.21,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6k59_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6k59"},{"model_identifier":"1t0c","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-04-08","sequence_identity":100.0,"uniprot_start":57,"uniprot_end":87,"resolution":null,"coverage":36.05,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1t0c_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1t0c"},{"model_identifier":"5mhd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-11-24","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":55,"resolution":null,"coverage":36.05,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5mhd_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5mhd"},{"model_identifier":"2lgb","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-07-25","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":55,"resolution":null,"coverage":36.05,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2lgb_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2lgb"},{"model_identifier":"2mvd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-10-02","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2mvd_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2mvd"},{"model_identifier":"2mvc","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-10-02","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2mvc_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2mvc"},{"model_identifier":"1k3m","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-10-03","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1k3m_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1k3m"},{"model_identifier":"2juu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-09-03","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2juu_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2juu"},{"model_identifier":"1hiq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1993-03-05","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1hiq_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1hiq"},{"model_identifier":"2juv","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-09-05","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2juv_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2juv"},{"model_identifier":"2rn5","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-12-06","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2rn5_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2rn5"},{"model_identifier":"2kxk","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-05-07","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2kxk_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2kxk"},{"model_identifier":"2hiu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-10-08","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2hiu_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2hiu"},{"model_identifier":"2l1y","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-08-09","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2l1y_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2l1y"},{"model_identifier":"2l1z","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-08-09","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2l1z_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2l1z"},{"model_identifier":"2kju","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-06-10","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2kju_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2kju"},{"model_identifier":"1xgl","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-10-10","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1xgl_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1xgl"},{"model_identifier":"1jco","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-06-11","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1jco_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1jco"},{"model_identifier":"2jv1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-09-11","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2jv1_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2jv1"},{"model_identifier":"2kqq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-11-13","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2kqq_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2kqq"},{"model_identifier":"1fu2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-09-13","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1fu2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY POWDER DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1fu2"},{"model_identifier":"1kmf","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-12-14","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1kmf_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1kmf"},{"model_identifier":"1vkt","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-10-14","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1vkt_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1vkt"},{"model_identifier":"1fub","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-09-14","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1fub_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY POWDER DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1fub"},{"model_identifier":"2n2x","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-15","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2n2x_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2n2x"},{"model_identifier":"2n2v","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-15","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2n2v_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2n2v"},{"model_identifier":"2n2w","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-15","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2n2w_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2n2w"},{"model_identifier":"1t1k","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-04-16","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1t1k_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1t1k"},{"model_identifier":"1t1p","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-04-16","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1t1p_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1t1p"},{"model_identifier":"1t1q","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-04-16","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1t1q_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1t1q"},{"model_identifier":"2mpg","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-05-17","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2mpg_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2mpg"},{"model_identifier":"1sf1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-02-19","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1sf1_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1sf1"},{"model_identifier":"2mpi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-05-19","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2mpi_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2mpi"},{"model_identifier":"2jmn","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-11-21","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2jmn_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2jmn"},{"model_identifier":"2k9r","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-10-23","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2k9r_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2k9r"},{"model_identifier":"6x4x","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-05-24","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6x4x_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6x4x"},{"model_identifier":"1lkq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2002-04-25","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1lkq_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1lkq"},{"model_identifier":"2m1d","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-11-26","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m1d_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m1d"},{"model_identifier":"2m1e","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-11-26","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m1e_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m1e"},{"model_identifier":"2mli","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-02-27","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2mli_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2mli"},{"model_identifier":"2hh4","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-06-27","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2hh4_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2hh4"},{"model_identifier":"2m2m","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-12-28","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m2m_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m2m"},{"model_identifier":"2aiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-12-28","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2aiy_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2aiy"},{"model_identifier":"1hit","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1992-02-28","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1hit_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1hit"},{"model_identifier":"2hho","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-06-28","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2hho_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2hho"},{"model_identifier":"1hls","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1995-06-28","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1hls_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1hls"},{"model_identifier":"2m2n","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-12-29","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m2n_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m2n"},{"model_identifier":"2m2o","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-12-29","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m2o_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m2o"},{"model_identifier":"2m2p","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-12-29","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m2p_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m2p"},{"model_identifier":"3aiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-12-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3aiy_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3aiy"},{"model_identifier":"4aiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-12-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4aiy_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4aiy"},{"model_identifier":"5aiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-12-29","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5aiy_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5aiy"},{"model_identifier":"2kjj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-05-29","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2kjj_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2kjj"},{"model_identifier":"2k91","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-09-29","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2k91_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2k91"},{"model_identifier":"1ai0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1997-04-30","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1ai0_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1ai0"},{"model_identifier":"1aiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1997-04-30","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1aiy_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1aiy"},{"model_identifier":"2h67","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-05-30","sequence_identity":87.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2h67_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2h67"},{"model_identifier":"1mhi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1994-11-30","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1mhi_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1mhi"},{"model_identifier":"2jum","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-08-31","sequence_identity":90.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":34.88,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2jum_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2jum"},{"model_identifier":"1sjt","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1997-10-09","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":53,"resolution":null,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1sjt_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1sjt"},{"model_identifier":"1a7f","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-03-12","sequence_identity":93.0,"uniprot_start":25,"uniprot_end":53,"resolution":null,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1a7f_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1a7f"},{"model_identifier":"1iog","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-08-13","sequence_identity":86.0,"uniprot_start":25,"uniprot_end":53,"resolution":null,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1iog_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1iog"},{"model_identifier":"1ioh","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-08-13","sequence_identity":86.0,"uniprot_start":25,"uniprot_end":53,"resolution":null,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1ioh_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1ioh"},{"model_identifier":"1mhj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1994-11-30","sequence_identity":97.0,"uniprot_start":25,"uniprot_end":54,"resolution":null,"coverage":33.72,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1mhj_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1mhj"},{"model_identifier":"1hui","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-03-29","sequence_identity":89.0,"uniprot_start":26,"uniprot_end":53,"resolution":null,"coverage":32.56,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1hui_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1hui"},{"model_identifier":"1his","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1992-02-28","sequence_identity":100.0,"uniprot_start":25,"uniprot_end":49,"resolution":null,"coverage":29.07,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1his_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1his"},{"model_identifier":"2mvc","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-10-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2mvc_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2mvc"},{"model_identifier":"2mvd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-10-02","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2mvd_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2mvd"},{"model_identifier":"1k3m","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-10-03","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1k3m_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1k3m"},{"model_identifier":"2juu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-09-03","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2juu_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2juu"},{"model_identifier":"1hiq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1993-03-05","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1hiq_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1hiq"},{"model_identifier":"2juv","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-09-05","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2juv_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2juv"},{"model_identifier":"2rn5","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-12-06","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2rn5_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2rn5"},{"model_identifier":"2kxk","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-05-07","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2kxk_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2kxk"},{"model_identifier":"2hiu","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-10-08","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2hiu_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2hiu"},{"model_identifier":"2l1z","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-08-09","sequence_identity":86.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2l1z_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2l1z"},{"model_identifier":"2l1y","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2010-08-09","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2l1y_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2l1y"},{"model_identifier":"1sjt","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1997-10-09","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1sjt_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1sjt"},{"model_identifier":"2kju","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-06-10","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2kju_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2kju"},{"model_identifier":"1xgl","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-10-10","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1xgl_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1xgl"},{"model_identifier":"1jco","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-06-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1jco_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1jco"},{"model_identifier":"2jv1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-09-11","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2jv1_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2jv1"},{"model_identifier":"1a7f","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-03-12","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1a7f_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1a7f"},{"model_identifier":"1iog","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-08-13","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1iog_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1iog"},{"model_identifier":"1ioh","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-08-13","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1ioh_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1ioh"},{"model_identifier":"2kqq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-11-13","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2kqq_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2kqq"},{"model_identifier":"1fu2","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-09-13","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1fu2_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY POWDER DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1fu2"},{"model_identifier":"1kmf","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2001-12-14","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1kmf_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1kmf"},{"model_identifier":"1vkt","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-10-14","sequence_identity":90.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1vkt_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1vkt"},{"model_identifier":"1fub","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2000-09-14","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1fub_updated.cif","model_format":"MMCIF","experimental_method":"X-RAY POWDER DIFFRACTION","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1fub"},{"model_identifier":"2n2v","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-15","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2n2v_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2n2v"},{"model_identifier":"2n2w","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-15","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2n2w_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2n2w"},{"model_identifier":"2n2x","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2015-05-15","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2n2x_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2n2x"},{"model_identifier":"1t1k","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-04-16","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1t1k_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1t1k"},{"model_identifier":"1t1p","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-04-16","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1t1p_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1t1p"},{"model_identifier":"1t1q","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-04-16","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1t1q_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1t1q"},{"model_identifier":"2mpg","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-05-17","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2mpg_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2mpg"},{"model_identifier":"1sf1","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2004-02-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1sf1_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1sf1"},{"model_identifier":"5mwq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2017-01-19","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5mwq_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5mwq"},{"model_identifier":"2mpi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-05-19","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2mpi_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2mpi"},{"model_identifier":"2jmn","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-11-21","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2jmn_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2jmn"},{"model_identifier":"2k9r","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-10-23","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2k9r_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2k9r"},{"model_identifier":"6x4x","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2020-05-24","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6x4x_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6x4x"},{"model_identifier":"5mhd","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2016-11-24","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5mhd_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5mhd"},{"model_identifier":"1lkq","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2002-04-25","sequence_identity":90.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1lkq_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1lkq"},{"model_identifier":"2lgb","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2011-07-25","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2lgb_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2lgb"},{"model_identifier":"2m1d","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-11-26","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m1d_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m1d"},{"model_identifier":"2m1e","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-11-26","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m1e_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m1e"},{"model_identifier":"2mli","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2014-02-27","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2mli_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2mli"},{"model_identifier":"2hh4","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-06-27","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2hh4_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2hh4"},{"model_identifier":"2m2m","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-12-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m2m_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m2m"},{"model_identifier":"2aiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-12-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2aiy_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2aiy"},{"model_identifier":"1his","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1992-02-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1his_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1his"},{"model_identifier":"1hit","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1992-02-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1hit_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1hit"},{"model_identifier":"2hho","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-06-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2hho_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2hho"},{"model_identifier":"1hls","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1995-06-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1hls_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1hls"},{"model_identifier":"2m2n","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-12-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m2n_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m2n"},{"model_identifier":"2m2o","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-12-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m2o_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m2o"},{"model_identifier":"2m2p","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2012-12-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2m2p_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2m2p"},{"model_identifier":"3aiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-12-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/3aiy_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/3aiy"},{"model_identifier":"4aiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-12-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/4aiy_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/4aiy"},{"model_identifier":"5aiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1998-12-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/5aiy_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/5aiy"},{"model_identifier":"1hui","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1996-03-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1hui_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1hui"},{"model_identifier":"2kjj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2009-05-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2kjj_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2kjj"},{"model_identifier":"2k91","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2008-09-29","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2k91_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2k91"},{"model_identifier":"1ai0","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1997-04-30","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1ai0_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1ai0"},{"model_identifier":"1aiy","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1997-04-30","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1aiy_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1aiy"},{"model_identifier":"2h67","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2006-05-30","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2h67_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2h67"},{"model_identifier":"1mhi","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1994-11-30","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1mhi_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1mhi"},{"model_identifier":"1mhj","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"1994-11-30","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/1mhj_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/1mhj"},{"model_identifier":"2jum","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2007-08-31","sequence_identity":95.0,"uniprot_start":90,"uniprot_end":110,"resolution":null,"coverage":24.42,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/2jum_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/2jum"},{"model_identifier":"6k59","model_category":"EXPERIMENTALLY DETERMINED","provider":"PDBe","created":"2019-05-28","sequence_identity":100.0,"uniprot_start":90,"uniprot_end":109,"resolution":null,"coverage":23.26,"model_url":"https://www.ebi.ac.uk/pdbe/static/entry/6k59_updated.cif","model_format":"MMCIF","experimental_method":"SOLUTION NMR","model_page_url":"https://www.ebi.ac.uk/pdbe/entry/pdb/6k59"},{"model_identifier":"PED00101e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":26,"uniprot_end":53,"model_url":"https://proteinensemble.org/api/ensemble/PED00101e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00101","number_of_conformers":25,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00101e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00095e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":90,"uniprot_end":110,"model_url":"https://proteinensemble.org/api/ensemble/PED00095e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00095","number_of_conformers":25,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00095e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00102e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":25,"uniprot_end":54,"model_url":"https://proteinensemble.org/api/ensemble/PED00102e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00102","number_of_conformers":40,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00102e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00103e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":25,"uniprot_end":54,"model_url":"https://proteinensemble.org/api/ensemble/PED00103e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00103","number_of_conformers":30,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00103e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00093e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":25,"uniprot_end":54,"model_url":"https://proteinensemble.org/api/ensemble/PED00093e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00093","number_of_conformers":25,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00093e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00094e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":90,"uniprot_end":110,"model_url":"https://proteinensemble.org/api/ensemble/PED00094e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00094","number_of_conformers":25,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00094e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00096e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":25,"uniprot_end":54,"model_url":"https://proteinensemble.org/api/ensemble/PED00096e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00096","number_of_conformers":35,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00096e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00096e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":90,"uniprot_end":110,"model_url":"https://proteinensemble.org/api/ensemble/PED00096e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00096","number_of_conformers":35,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00096e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00101e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":90,"uniprot_end":110,"model_url":"https://proteinensemble.org/api/ensemble/PED00101e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00101","number_of_conformers":25,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00101e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00103e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":90,"uniprot_end":110,"model_url":"https://proteinensemble.org/api/ensemble/PED00103e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00103","number_of_conformers":30,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00103e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00093e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":90,"uniprot_end":110,"model_url":"https://proteinensemble.org/api/ensemble/PED00093e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00093","number_of_conformers":25,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00093e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00102e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":90,"uniprot_end":110,"model_url":"https://proteinensemble.org/api/ensemble/PED00102e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00102","number_of_conformers":40,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00102e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00104e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":90,"uniprot_end":110,"model_url":"https://proteinensemble.org/api/ensemble/PED00104e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00104","number_of_conformers":50,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00104e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00095e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":25,"uniprot_end":54,"model_url":"https://proteinensemble.org/api/ensemble/PED00095e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00095","number_of_conformers":25,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00095e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00094e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":25,"uniprot_end":54,"model_url":"https://proteinensemble.org/api/ensemble/PED00094e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00094","number_of_conformers":25,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00094e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"PED00104e000","model_category":"CONFORMATIONAL ENSEMBLE","provider":"PED","created":"2020-10-01","uniprot_start":25,"uniprot_end":54,"model_url":"https://proteinensemble.org/api/ensemble/PED00104e000","model_format":"PDB","model_page_url":"https://proteinensemble.org/PED00104","number_of_conformers":50,"ensemble_sample_url":"https://proteinensemble.org/api/ensemble_sample/PED00104e000","ensemble_sample_format":"MMCIF"},{"model_identifier":"614833d3963a0435809066a9","model_category":"TEMPLATE-BASED","provider":"SWISS-MODEL","created":"2021-09-20","sequence_identity":1.0,"uniprot_start":25,"uniprot_end":110,"coverage":0.782,"confidence_version":"4.2.0","confidence_avg_local_score":0.526,"model_url":"https://swissmodel.expasy.org/3d-beacons/uniprot/P01308.pdb?range=25-110&template=2lwz.1.A&provider=swissmodel","model_format":"PDB","model_page_url":"https://swissmodel.expasy.org/repository/uniprot/P01308?provider=swissmodelrange=25-110&template=2lwz.1.A","confidence_type":"QMEANDisCo"},{"model_identifier":"AF-P01308-F1","model_category":"DEEP-LEARNING","provider":"AlphaFold DB","created":"2021-07-01","sequence_identity":1.0,"uniprot_start":1,"uniprot_end":110,"coverage":100.0,"model_url":"https://alphafold.ebi.ac.uk/files/AF-P01308-F1-model_v1.cif","model_format":"MMCIF","model_page_url":"https://alphafold.ebi.ac.uk/entry/P01308"},{"model_identifier":"2400","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2018-11-11","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDE25_fit1_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDE25"},{"model_identifier":"2396","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2018-11-11","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDE25_fit2_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDE25"},{"model_identifier":"2452","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDEV5_fit2_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDEV5"},{"model_identifier":"2453","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDEV5_fit3_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDEV5"},{"model_identifier":"2456","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDEW5_fit2_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDEW5"},{"model_identifier":"2457","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDEW5_fit3_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDEW5"},{"model_identifier":"2459","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDEX5_fit2_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDEX5"},{"model_identifier":"2460","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDEX5_fit3_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDEX5"},{"model_identifier":"2462","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":609,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDEY5_fit2_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDEY5"},{"model_identifier":"2463","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":609,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDEY5_fit3_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDEY5"},{"model_identifier":"2465","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":609,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDEZ5_fit2_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDEZ5"},{"model_identifier":"2467","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":609,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDEZ5_fit3_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDEZ5"},{"model_identifier":"2469","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":609,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDE26_fit2_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDE26"},{"model_identifier":"2470","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2019-04-07","uniprot_start":25,"uniprot_end":609,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDE26_fit3_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDE26"},{"model_identifier":"4420","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2020-12-18","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDJY3_fit1_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDJY3"},{"model_identifier":"4421","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2020-12-18","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDJY3_fit1_model2.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDJY3"},{"model_identifier":"4422","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2020-12-18","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDJZ3_fit1_model1.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDJZ3"},{"model_identifier":"4423","model_category":"EXPERIMENTALLY DETERMINED","provider":"SASBDB","created":"2020-12-18","uniprot_start":25,"uniprot_end":110,"model_url":"https://www.sasbdb.org/media/pdb_file/SASDJZ3_fit1_model2.pdb","model_format":"PDB","model_page_url":"https://www.sasbdb.org/data/SASDJZ3"}]}
index a03819d..b0fc116 100644 (file)
@@ -24,6 +24,17 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
+import java.awt.EventQueue;
+import java.awt.FontMetrics;
+import java.awt.event.MouseEvent;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.swing.JLabel;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
 import jalview.api.AlignViewportI;
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
@@ -33,6 +44,8 @@ import jalview.commands.EditCommand.Edit;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SearchResults;
+import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceI;
 import jalview.gui.SeqPanel.MousePos;
@@ -40,17 +53,6 @@ import jalview.io.DataSourceType;
 import jalview.io.FileLoader;
 import jalview.util.MessageManager;
 import jalview.viewmodel.ViewportRanges;
-
-import java.awt.EventQueue;
-import java.awt.event.MouseEvent;
-import java.lang.reflect.InvocationTargetException;
-
-import javax.swing.JLabel;
-
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
 import junit.extensions.PA;
 
 public class SeqPanelTest
@@ -635,6 +637,7 @@ public class SeqPanelTest
   {
     Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "false");
     Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
+    Cache.applicationProperties.setProperty("FONT_SIZE", "10");
     AlignFrame alignFrame = new FileLoader().LoadFileWaitTillLoaded(
             "examples/uniref50.fa", DataSourceType.FILE);
     AlignViewportI av = alignFrame.getViewport();
@@ -902,4 +905,157 @@ public class SeqPanelTest
       e.printStackTrace();
     }
   }
+  @Test(groups = "Functional")
+  public void testFindMousePosition_wrapped_scales_longSequence()
+  {
+    Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", "false");
+    Cache.applicationProperties.setProperty("WRAP_ALIGNMENT", "true");
+    Cache.applicationProperties.setProperty("FONT_SIZE", "14");
+    Cache.applicationProperties.setProperty("FONT_NAME", "SansSerif");
+    Cache.applicationProperties.setProperty("FONT_STYLE", "0");
+    // sequence of 50 bases, doubled 10 times, = 51200 bases
+    String dna = "ATGGCCATTGGGCCCAAATTTCCCAAAGGGTTTCCCTGAGGTCAGTCAGA";
+    for (int i = 0 ; i < 10 ; i++)
+    {
+      dna += dna;
+    }
+    assertEquals(dna.length(), 51200);
+    AlignFrame alignFrame = new FileLoader()
+            .LoadFileWaitTillLoaded(dna, DataSourceType.PASTE);
+    SeqPanel testee = alignFrame.alignPanel.getSeqPanel();
+    AlignViewport av = alignFrame.getViewport();
+    av.setScaleAboveWrapped(true);
+    av.setScaleLeftWrapped(true);
+    av.setScaleRightWrapped(true);
+    alignFrame.alignPanel.updateLayout();
+
+    try
+    {
+      Thread.sleep(200);
+    } catch (InterruptedException e)
+    {
+    }
+  
+    final int charHeight = av.getCharHeight();
+    final int charWidth = av.getCharWidth();
+    assertEquals(charHeight, 17);
+    assertEquals(charWidth, 12);
+    
+    FontMetrics fm = testee.getFontMetrics(av.getFont());
+    int labelWidth = fm.stringWidth("00000") + charWidth;
+    assertEquals(labelWidth, 57); // 5 x 9 + charWidth
+    assertEquals(testee.seqCanvas.getLabelWidthWest(), labelWidth);
+
+    int x = 0;
+    int y = 0;
+  
+    /*
+     * mouse at top left of wrapped panel; there is a gap of 2 * charHeight
+     * above the alignment
+     */
+    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, -1); // over scale left, not an alignment column
+    assertEquals(pos.seqIndex, -1); // above sequences
+    assertEquals(pos.annotationIndex, -1);
+
+    /*
+     * cursor over scale above first sequence
+     */
+    y += charHeight;
+    x = labelWidth;
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
+            false, 0);
+    pos = testee.findMousePosition(evt);
+    assertEquals(pos.seqIndex, -1);
+    assertEquals(pos.column, 0);
+    assertEquals(pos.annotationIndex, -1);
+    
+    /*
+     * cursor over scale left of first sequence
+     */
+    y += charHeight;
+    x = 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);
+    assertEquals(pos.column, -1);
+    assertEquals(pos.annotationIndex, -1);
+  
+    /*
+     * cursor over start of first sequence
+     */
+    x = labelWidth;
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
+            false, 0);
+    pos = testee.findMousePosition(evt);
+    assertEquals(pos.seqIndex, 0);
+    assertEquals(pos.column, 0);
+    assertEquals(pos.annotationIndex, -1);
+  
+    /*
+     * move one character right, to bottom pixel of same row
+     */
+    x += charWidth;
+    y += charHeight - 1;
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
+            false, 0);
+    pos = testee.findMousePosition(evt);
+    assertEquals(pos.seqIndex, 0);
+    assertEquals(pos.column, 1);
+    
+    /*
+     * move down one pixel - now in the no man's land between rows
+     */
+    y += 1;
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
+            false, 0);
+    pos = testee.findMousePosition(evt);
+    assertEquals(pos.seqIndex, -1);
+    assertEquals(pos.column, 1);
+    
+    /*
+     * move down two char heights less one pixel - still in the no man's land
+     * (scale above + spacer line)
+     */
+    y += (2 * charHeight - 1);
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
+            false, 0);
+    pos = testee.findMousePosition(evt);
+    assertEquals(pos.seqIndex, -1);
+    assertEquals(pos.column, 1);
+    
+    /*
+     * move down one more pixel - now on the next row of the sequence
+     */
+    y += 1;
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
+            false, 0);
+    pos = testee.findMousePosition(evt);
+    assertEquals(pos.seqIndex, 0);
+    assertEquals(pos.column, 1 + av.getWrappedWidth());
+    
+    /*
+     * scroll to near the end of the sequence
+     */
+    SearchResultsI sr = new SearchResults();
+    int scrollTo = dna.length() - 1000;
+    sr.addResult(av.getAlignment().getSequenceAt(0), scrollTo, scrollTo); 
+    alignFrame.alignPanel.scrollToPosition(sr);
+    
+    /*
+     * place the mouse on the first column of the 6th sequence, and
+     * verify that (computed) findMousePosition matches (actual) ViewportRanges
+     */
+    x = labelWidth;
+    y = 17 * charHeight; // 17 = 6 times two header rows and 5 sequence rows
+    evt = new MouseEvent(testee, MouseEvent.MOUSE_MOVED, 0L, 0, x, y, 0, 0, 0,
+            false, 0);
+    pos = testee.findMousePosition(evt);
+    assertEquals(pos.seqIndex, 0);
+    int expected = av.getRanges().getStartRes() + 5 * av.getWrappedWidth();
+    assertEquals(pos.column, expected);
+  }
 }
index 073bde8..a87e1c6 100644 (file)
  */
 package jalview.gui;
 
-import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.Assert.assertEquals;
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.datamodel.DBRefEntry;
-import jalview.datamodel.DBRefSource;
-import jalview.datamodel.PDBEntry;
-import jalview.datamodel.Sequence;
-import jalview.datamodel.SequenceI;
-import jalview.fts.api.FTSData;
-import jalview.jbgui.GStructureChooser.FilterOption;
-import jalview.ws.params.InvalidArgumentException;
-
 import java.util.Collection;
 import java.util.Vector;
 
+import org.junit.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.core.FTSRestClient;
+import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.fts.service.pdb.PDBFTSRestClientTest;
+import jalview.fts.service.threedbeacons.TDBeaconsFTSRestClient;
+import jalview.fts.threedbeacons.TDBeaconsFTSRestClientTest;
+import jalview.gui.structurechooser.PDBStructureChooserQuerySource;
+import jalview.jbgui.FilterOption;
 import junit.extensions.PA;
 
+@Test(singleThreaded = true)
 public class StructureChooserTest
 {
 
@@ -53,7 +58,7 @@ public class StructureChooserTest
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
-  Sequence seq;
+  Sequence seq,upSeq;
 
   @BeforeMethod(alwaysRun = true)
   public void setUp() throws Exception
@@ -75,60 +80,73 @@ public class StructureChooserTest
     pdbIds.add(dbRef);
 
     seq.setPDBId(pdbIds);
+    
+    // Uniprot sequence for 3D-Beacons mocks
+    upSeq = new Sequence("P38398", 
+            "MDLSALRVEEVQNVINAMQKILECPICLELIKEPVSTKCDHIFCKFCMLKLLNQKKGPSQCPLCKNDITKRS\n"
+            + "LQESTRFSQLVEELLKIICAFQLDTGLEYANSYNFAKKENNSPEHLKDEVSIIQSMGYRNRAKRLLQSEPEN\n"
+            + "PSLQETSLSVQLSNLGTVRTLRTKQRIQPQKTSVYIELGSDSSEDTVNKATYCSVGDQELLQITPQGTRDEI\n"
+            + "SLDSAKKAACEFSETDVTNTEHHQPSNNDLNTTEKRAAERHPEKYQGSSVSNLHVEPCGTNTHASSLQHENS\n"
+            + "SLLLTKDRMNVEKAEFCNKSKQPGLARSQHNRWAGSKETCNDRRTPSTEKKVDLNADPLCERKEWNKQKLPC\n"
+            + "SENPRDTEDVPWITLNSSIQKVNEWFSRSDELLGSDDSHDGESESNAKVADVLDVLNEVDEYSGSSEKIDLL\n"
+            + "ASDPHEALICKSERVHSKSVESNIEDKIFGKTYRKKASLPNLSHVTENLIIGAFVTEPQIIQERPLTNKLKR\n"
+            + "KRRPTSGLHPEDFIKKADLAVQKTPEMINQGTNQTEQNGQVMNITNSGHENKTKGDSIQNEKNPNPIESLEK\n"
+            + "ESAFKTKAEPISSSISNMELELNIHNSKAPKKNRLRRKSSTRHIHALELVVSRNLSPPNCTELQIDSCSSSE\n"
+            + "EIKKKKYNQMPVRHSRNLQLMEGKEPATGAKKSNKPNEQTSKRHDSDTFPELKLTNAPGSFTKCSNTSELKE\n"
+            + "FVNPSLPREEKEEKLETVKVSNNAEDPKDLMLSGERVLQTERSVESSSISLVPGTDYGTQESISLLEVSTLG\n"
+            + "KAKTEPNKCVSQCAAFENPKGLIHGCSKDNRNDTEGFKYPLGHEVNHSRETSIEMEESELDAQYLQNTFKVS\n"
+            + "KRQSFAPFSNPGNAEEECATFSAHSGSLKKQSPKVTFECEQKEENQGKNESNIKPVQTVNITAGFPVVGQKD\n"
+            + "KPVDNAKCSIKGGSRFCLSSQFRGNETGLITPNKHGLLQNPYRIPPLFPIKSFVKTKCKKNLLEENFEEHSM\n"
+            + "SPEREMGNENIPSTVSTISRNNIRENVFKEASSSNINEVGSSTNEVGSSINEIGSSDENIQAELGRNRGPKL\n"
+            + "NAMLRLGVLQPEVYKQSLPGSNCKHPEIKKQEYEEVVQTVNTDFSPYLISDNLEQPMGSSHASQVCSETPDD\n"
+            + "LLDDGEIKEDTSFAENDIKESSAVFSKSVQKGELSRSPSPFTHTHLAQGYRRGAKKLESSEENLSSEDEELP\n"
+            + "CFQHLLFGKVNNIPSQSTRHSTVATECLSKNTEENLLSLKNSLNDCSNQVILAKASQEHHLSEETKCSASLF\n"
+            + "SSQCSELEDLTANTNTQDPFLIGSSKQMRHQSESQGVGLSDKELVSDDEERGTGLEENNQEEQSMDSNLGEA\n"
+            + "ASGCESETSVSEDCSGLSSQSDILTTQQRDTMQHNLIKLQQEMAELEAVLEQHGSQPSNSYPSIISDSSALE\n"
+            + "DLRNPEQSTSEKAVLTSQKSSEYPISQNPEGLSADKFEVSADSSTSKNKEPGVERSSPSKCPSLDDRWYMHS\n"
+            + "CSGSLQNRNYPSQEELIKVVDVEEQQLEESGPHDLTETSYLPRQDLEGTPYLESGISLFSDDPESDPSEDRA\n"
+            + "PESARVGNIPSSTSALKVPQLKVAESAQSPAAAHTTDTAGYNAMEESVSREKPELTASTERVNKRMSMVVSG\n"
+            + "LTPEEFMLVYKFARKHHITLTNLITEETTHVVMKTDAEFVCERTLKYFLGIAGGKWVVSYFWVTQSIKERKM\n"
+            + "LNEHDFEVRGDVVNGRNHQGPKRARESQDRKIFRGLEICCYGPFTNMPTDQLEWMVQLCGASVVKELSSFTL\n"
+            + "GTGVHPIVVVQPDAWTEDNGFHAIGQMCEAPVVTREWVLDSVALYQCQELDTYLIPQIPHSHY\n"
+            + "", 1,
+1863);
+    upSeq.createDatasetSequence();
+    upSeq.setDescription("Breast cancer type 1 susceptibility protein");
+    upSeq.addDBRef(new DBRefEntry("UNIPROT","0","P38398",null,true));
   }
 
   @AfterMethod(alwaysRun = true)
   public void tearDown() throws Exception
   {
     seq = null;
-  }
-
-  @Test(groups = { "Functional" })
-  public void buildQueryTest()
-  {
-    String query = StructureChooser.buildQuery(seq);
-    assertEquals("pdb_id:1tim", query);
-    System.out.println("seq >>>> " + seq);
-    seq.getAllPDBEntries().clear();
-    query = StructureChooser.buildQuery(seq);
-    assertEquals(
-            "text:XYZ_1 OR text:XYZ_2 OR text:XYZ_3 OR text:XYZ_4 OR text:4kqy",
-            query);
-       seq.setDBRefs(null);
-    query = StructureChooser.buildQuery(seq);
-    assertEquals("text:4kqy", query);
-
-    DBRefEntry uniprotDBRef = new DBRefEntry();
-    uniprotDBRef.setAccessionId("P12345");
-    uniprotDBRef.setSource(DBRefSource.UNIPROT);
-    seq.addDBRef(uniprotDBRef);
-
-    DBRefEntry pdbDBRef = new DBRefEntry();
-    pdbDBRef.setAccessionId("1XYZ");
-    pdbDBRef.setSource(DBRefSource.PDB);
-    seq.addDBRef(pdbDBRef);
-
-    for (int x = 1; x < 5; x++)
-    {
-      DBRefEntry dbRef = new DBRefEntry();
-      dbRef.setAccessionId("XYZ_" + x);
-      seq.addDBRef(dbRef);
-    }
-    query = StructureChooser.buildQuery(seq);
-    assertEquals(
-            "uniprot_accession:P12345 OR uniprot_id:P12345 OR pdb_id:1xyz",
-            query);
+    upSeq=null;
   }
 
   @Test(groups = { "Functional" })
   public void populateFilterComboBoxTest() throws InterruptedException
   {
+    TDBeaconsFTSRestClientTest.setMock();
+    PDBFTSRestClientTest.setMock();
+
     SequenceI[] selectedSeqs = new SequenceI[] { seq };
     StructureChooser sc = new StructureChooser(selectedSeqs, seq, null);
+    ThreadwaitFor(200, sc);
+    
+    // if structures are not discovered then don't
+    // populate filter options
     sc.populateFilterComboBox(false, false);
     int optionsSize = sc.getCmbFilterOption().getItemCount();
-    assertEquals(2, optionsSize); // if structures are not discovered then don't
-                                  // populate filter options
+    System.out.println("Items (no data, no cache): ");
+    StringBuilder items = new StringBuilder();
+    for (int p=0;p<optionsSize;p++)
+    {
+      items.append
+      ("- ").append(sc.getCmbFilterOption().getItemAt(p).getName()).append("\n");
+
+    }
+    // report items when this fails - seems to be a race condition
+    Assert.assertEquals(items.toString(),optionsSize,2); 
 
     sc.populateFilterComboBox(true, false);
     optionsSize = sc.getCmbFilterOption().getItemCount();
@@ -140,36 +158,75 @@ public class StructureChooserTest
     FilterOption filterOpt = (FilterOption) sc.getCmbFilterOption()
             .getSelectedItem();
     assertEquals("Cached Structures", filterOpt.getName());
+    FTSRestClient.unMock((FTSRestClient) TDBeaconsFTSRestClient.getInstance());
+    FTSRestClient.unMock((FTSRestClient) PDBFTSRestClient.getInstance());
+
   }
 
   @Test(groups = { "Network" })
   public void fetchStructuresInfoTest()
   {
+    FTSRestClient.unMock((FTSRestClient) TDBeaconsFTSRestClient.getInstance());
+    PDBFTSRestClient.unMock((FTSRestClient) PDBFTSRestClient.getInstance());
     SequenceI[] selectedSeqs = new SequenceI[] { seq };
     StructureChooser sc = new StructureChooser(selectedSeqs, seq, null);
+    // not mocked, wait for 2s 
+    ThreadwaitFor(2000, sc);
+    
     sc.fetchStructuresMetaData();
     Collection<FTSData> ss = (Collection<FTSData>) PA.getValue(sc,
             "discoveredStructuresSet");
     assertNotNull(ss);
     assertTrue(ss.size() > 0);
+  }
 
+  @Test(groups = { "Functional" })
+  public void fetchStructuresInfoMockedTest()
+  {
+    TDBeaconsFTSRestClientTest.setMock();
+    PDBFTSRestClientTest.setMock();
+    SequenceI[] selectedSeqs = new SequenceI[] { upSeq };
+    StructureChooser sc = new StructureChooser(selectedSeqs, seq, null);
+    ThreadwaitFor(500, sc);
+    
+    sc.fetchStructuresMetaData();
+    Collection<FTSData> ss = (Collection<FTSData>) PA.getValue(sc,
+            "discoveredStructuresSet");
+    assertNotNull(ss);
+    assertTrue(ss.size() > 0);
+  }
+
+  private void ThreadwaitFor(int i, StructureChooser sc)
+  {
+    long timeout = i+System.currentTimeMillis();
+    while (!sc.isDialogVisible() && timeout > System.currentTimeMillis())
+    {
+      try {
+        Thread.sleep(50);
+      } catch (InterruptedException x)
+      {
+        
+      }
+    }
+    
   }
 
+
   @Test(groups = { "Functional" })
   public void sanitizeSeqNameTest()
   {
     String name = "ab_cdEF|fwxyz012349";
-    assertEquals(name, StructureChooser.sanitizeSeqName(name));
+    assertEquals(name, PDBStructureChooserQuerySource.sanitizeSeqName(name));
 
     // remove a [nn] substring
     name = "abcde12[345]fg";
-    assertEquals("abcde12fg", StructureChooser.sanitizeSeqName(name));
+    assertEquals("abcde12fg", PDBStructureChooserQuerySource.sanitizeSeqName(name));
 
     // remove characters other than a-zA-Z0-9 | or _
     name = "ab[cd],.\t£$*!- \\\"@:e";
-    assertEquals("abcde", StructureChooser.sanitizeSeqName(name));
+    assertEquals("abcde", PDBStructureChooserQuerySource.sanitizeSeqName(name));
 
     name = "abcde12[345a]fg";
-    assertEquals("abcde12345afg", StructureChooser.sanitizeSeqName(name));
+    assertEquals("abcde12345afg", PDBStructureChooserQuerySource.sanitizeSeqName(name));
   }
 }
diff --git a/test/jalview/gui/structurechooser/StructureChooserQuerySourceTest.java b/test/jalview/gui/structurechooser/StructureChooserQuerySourceTest.java
new file mode 100644 (file)
index 0000000..1912f14
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.gui.structurechooser;
+
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Vector;
+
+import org.junit.Assert;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
+import jalview.datamodel.PDBEntry;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.fts.api.FTSData;
+import jalview.fts.core.FTSRestRequest;
+import jalview.fts.core.FTSRestResponse;
+import jalview.fts.service.pdb.PDBFTSRestClient;
+import jalview.fts.service.pdb.PDBFTSRestClientTest;
+import jalview.fts.threedbeacons.TDBeaconsFTSRestClientTest;
+import jalview.gui.JvOptionPane;
+import jalview.gui.StructureChooser;
+import jalview.jbgui.FilterOption;
+
+public class StructureChooserQuerySourceTest
+{
+
+  @BeforeClass(alwaysRun = true)
+  public void setUpJvOptionPane()
+  {
+    JvOptionPane.setInteractiveMode(false);
+    JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
+  }
+
+  Sequence seq,upSeq,upSeq_insulin;
+
+  // same set up as for structurechooser test
+  
+@BeforeMethod(alwaysRun = true)
+  public void setUp() throws Exception
+  {
+    seq = new Sequence("PDB|4kqy|4KQY|A", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 1,
+            26);
+    seq.createDatasetSequence();
+    for (int x = 1; x < 5; x++)
+    {
+      DBRefEntry dbRef = new DBRefEntry();
+      dbRef.setAccessionId("XYZ_" + x);
+      seq.addDBRef(dbRef);
+    }
+
+    PDBEntry dbRef = new PDBEntry();
+    dbRef.setId("1tim");
+
+    Vector<PDBEntry> pdbIds = new Vector<>();
+    pdbIds.add(dbRef);
+
+    seq.setPDBId(pdbIds);
+    
+    // Uniprot sequence for 3D-Beacons mocks
+    upSeq = new Sequence("P38398", 
+            "MDLSALRVEEVQNVINAMQKILECPICLELIKEPVSTKCDHIFCKFCMLKLLNQKKGPSQCPLCKNDITKRS\n"
+            + "LQESTRFSQLVEELLKIICAFQLDTGLEYANSYNFAKKENNSPEHLKDEVSIIQSMGYRNRAKRLLQSEPEN\n"
+            + "PSLQETSLSVQLSNLGTVRTLRTKQRIQPQKTSVYIELGSDSSEDTVNKATYCSVGDQELLQITPQGTRDEI\n"
+            + "SLDSAKKAACEFSETDVTNTEHHQPSNNDLNTTEKRAAERHPEKYQGSSVSNLHVEPCGTNTHASSLQHENS\n"
+            + "SLLLTKDRMNVEKAEFCNKSKQPGLARSQHNRWAGSKETCNDRRTPSTEKKVDLNADPLCERKEWNKQKLPC\n"
+            + "SENPRDTEDVPWITLNSSIQKVNEWFSRSDELLGSDDSHDGESESNAKVADVLDVLNEVDEYSGSSEKIDLL\n"
+            + "ASDPHEALICKSERVHSKSVESNIEDKIFGKTYRKKASLPNLSHVTENLIIGAFVTEPQIIQERPLTNKLKR\n"
+            + "KRRPTSGLHPEDFIKKADLAVQKTPEMINQGTNQTEQNGQVMNITNSGHENKTKGDSIQNEKNPNPIESLEK\n"
+            + "ESAFKTKAEPISSSISNMELELNIHNSKAPKKNRLRRKSSTRHIHALELVVSRNLSPPNCTELQIDSCSSSE\n"
+            + "EIKKKKYNQMPVRHSRNLQLMEGKEPATGAKKSNKPNEQTSKRHDSDTFPELKLTNAPGSFTKCSNTSELKE\n"
+            + "FVNPSLPREEKEEKLETVKVSNNAEDPKDLMLSGERVLQTERSVESSSISLVPGTDYGTQESISLLEVSTLG\n"
+            + "KAKTEPNKCVSQCAAFENPKGLIHGCSKDNRNDTEGFKYPLGHEVNHSRETSIEMEESELDAQYLQNTFKVS\n"
+            + "KRQSFAPFSNPGNAEEECATFSAHSGSLKKQSPKVTFECEQKEENQGKNESNIKPVQTVNITAGFPVVGQKD\n"
+            + "KPVDNAKCSIKGGSRFCLSSQFRGNETGLITPNKHGLLQNPYRIPPLFPIKSFVKTKCKKNLLEENFEEHSM\n"
+            + "SPEREMGNENIPSTVSTISRNNIRENVFKEASSSNINEVGSSTNEVGSSINEIGSSDENIQAELGRNRGPKL\n"
+            + "NAMLRLGVLQPEVYKQSLPGSNCKHPEIKKQEYEEVVQTVNTDFSPYLISDNLEQPMGSSHASQVCSETPDD\n"
+            + "LLDDGEIKEDTSFAENDIKESSAVFSKSVQKGELSRSPSPFTHTHLAQGYRRGAKKLESSEENLSSEDEELP\n"
+            + "CFQHLLFGKVNNIPSQSTRHSTVATECLSKNTEENLLSLKNSLNDCSNQVILAKASQEHHLSEETKCSASLF\n"
+            + "SSQCSELEDLTANTNTQDPFLIGSSKQMRHQSESQGVGLSDKELVSDDEERGTGLEENNQEEQSMDSNLGEA\n"
+            + "ASGCESETSVSEDCSGLSSQSDILTTQQRDTMQHNLIKLQQEMAELEAVLEQHGSQPSNSYPSIISDSSALE\n"
+            + "DLRNPEQSTSEKAVLTSQKSSEYPISQNPEGLSADKFEVSADSSTSKNKEPGVERSSPSKCPSLDDRWYMHS\n"
+            + "CSGSLQNRNYPSQEELIKVVDVEEQQLEESGPHDLTETSYLPRQDLEGTPYLESGISLFSDDPESDPSEDRA\n"
+            + "PESARVGNIPSSTSALKVPQLKVAESAQSPAAAHTTDTAGYNAMEESVSREKPELTASTERVNKRMSMVVSG\n"
+            + "LTPEEFMLVYKFARKHHITLTNLITEETTHVVMKTDAEFVCERTLKYFLGIAGGKWVVSYFWVTQSIKERKM\n"
+            + "LNEHDFEVRGDVVNGRNHQGPKRARESQDRKIFRGLEICCYGPFTNMPTDQLEWMVQLCGASVVKELSSFTL\n"
+            + "GTGVHPIVVVQPDAWTEDNGFHAIGQMCEAPVVTREWVLDSVALYQCQELDTYLIPQIPHSHY\n"
+            + "", 1,
+1863);
+    upSeq.createDatasetSequence();
+    upSeq.setDescription("Breast cancer type 1 susceptibility protein");
+    upSeq.addDBRef(new DBRefEntry("UNIPROT","0","P38398",null,true));
+    
+    upSeq_insulin=new Sequence("INS_HUMAN",
+            "MALWMRLLPLLALLALWGPDPAAAFVNQHLCGSHLVEALYLVCGERGFFYTPKTRREAEDLQVGQVELGGGP"
+            + "GAGSLQPLALEGSLQKRGIVEQCCTSICSLYQLENYCN");
+    upSeq_insulin.createDatasetSequence();
+    upSeq_insulin.setDescription("Insulin");
+    upSeq_insulin.addDBRef(new DBRefEntry("UNIPROT","0","P01308",null,true));
+  }
+
+@AfterMethod(alwaysRun = true)
+  public void tearDown() throws Exception
+  {
+    seq = null;
+    upSeq=null;
+  }
+
+  @SuppressWarnings("deprecation")
+  @Test(groups = { "Functional" })
+  public void buildPDBQueryTest()
+  {
+    System.out.println("seq >>>> " + seq);
+    
+    StructureChooserQuerySource scquery = StructureChooserQuerySource.getQuerySourceFor(new SequenceI[] { seq});
+    AssertJUnit.assertTrue(scquery instanceof PDBStructureChooserQuerySource);
+    String query = scquery.buildQuery(seq);
+    AssertJUnit.assertEquals("pdb_id:1tim", query);
+    seq.getAllPDBEntries().clear();
+    query = scquery.buildQuery(seq);
+    AssertJUnit.assertEquals(
+            "text:XYZ_1 OR text:XYZ_2 OR text:XYZ_3 OR text:XYZ_4 OR text:4kqy",
+            query);
+    seq.setDBRefs(null);
+    query = scquery.buildQuery(seq);
+    System.out.println(query);
+    AssertJUnit.assertEquals("text:4kqy", query);
+
+    DBRefEntry uniprotDBRef = new DBRefEntry();
+    uniprotDBRef.setAccessionId("P12345");
+    uniprotDBRef.setSource(DBRefSource.UNIPROT);
+    seq.addDBRef(uniprotDBRef);
+
+    DBRefEntry pdbDBRef = new DBRefEntry();
+    pdbDBRef.setAccessionId("1XYZ");
+    pdbDBRef.setSource(DBRefSource.PDB);
+    seq.addDBRef(pdbDBRef);
+
+    for (int x = 1; x < 5; x++)
+    {
+      DBRefEntry dbRef = new DBRefEntry();
+      dbRef.setAccessionId("XYZ_" + x);
+      seq.addDBRef(dbRef);
+    }
+    System.out.println("");
+    System.out.println(seq.getDBRefs());
+    System.out.println(query);
+    query = scquery.buildQuery(seq);
+    AssertJUnit.assertEquals(
+            "uniprot_accession:P12345 OR uniprot_id:P12345 OR pdb_id:1xyz",
+            query);
+  }
+
+  @SuppressWarnings("deprecation")
+  @Test(groups = { "Functional" })
+  public void buildThreeDBQueryTest()
+  {
+    System.out.println("seq >>>> " + upSeq);
+    TDBeaconsFTSRestClientTest.setMock();
+    PDBFTSRestClientTest.setMock();
+    StructureChooserQuerySource scquery = StructureChooserQuerySource.getQuerySourceFor(new SequenceI[] { upSeq});
+    // gets the lightweight proxy rather than the ThreeDBStructureChooserQuerySource
+    AssertJUnit.assertTrue(scquery instanceof ThreeDBStructureChooserQuerySource);
+    String query = scquery.buildQuery(upSeq);
+    AssertJUnit.assertEquals("P38398", query);
+    
+    // query shouldn't change regardless of additional entries
+    // because 3DBeacons requires canonical entries.
+    upSeq.getAllPDBEntries().clear();
+    query = scquery.buildQuery(upSeq);
+    AssertJUnit.assertEquals("P38398", query);
+    upSeq.setDBRefs(null);
+    query = scquery.buildQuery(upSeq);
+    /*
+     * legacy projects/datasets will not have canonical flags set for uniprot dbrefs
+     * graceful behaviour would be to
+     *  - pick one ? not possible
+     *  - iterate through all until a 200 is obtained ?
+     *  ---> ideal but could be costly
+     *  ---> better to do a direct retrieval from uniprot to work out which is the canonical identifier..
+     *  ----> need a test to check that accessions can be promoted to canonical!
+     */
+    //FIXME - need to be able to use ID to query here ?
+    AssertJUnit.assertEquals(null, query);
+
+    
+    
+    // TODO: 
+    /**
+     * set of sequences:
+     * - no protein -> TDB not applicable, query PDBe only (consider RNA or DNA - specific query adapter ?)
+     * - protein but no uniprot -> first consider trying to get uniprot refs (need a mark to say none are available)
+     * - protein and uniprot - no canonicals -> resolve to uniprot automatically to get canonicals
+     * - query uniprot against 3DBeacons
+     * --> decorate experimental structures with additional data from PDBe
+     * - query remaining against PDBe
+     * Ranking
+     * - 3D Beacons
+     *  --> in memory ranking - no need to query twice
+     *  Rank by
+     *  - experimental > AlphaFold -> Model
+     *  - start > end
+     *  -> filters for 
+     *  -> experimental only
+     *  -> experimental plus best models for other regions
+     *  -> "best cover" 
+     *  -> need to be able to select correct reference (the longest one that covers all) for superposition
+     */
+//    
+//    DBRefEntry uniprotDBRef = new DBRefEntry();
+//    uniprotDBRef.setAccessionId("P12345");
+//    uniprotDBRef.setSource(DBRefSource.UNIPROT);
+//    upSeq.addDBRef(uniprotDBRef);
+//
+//    DBRefEntry pdbDBRef = new DBRefEntry();
+//    pdbDBRef.setAccessionId("1XYZ");
+//    pdbDBRef.setSource(DBRefSource.PDB);
+//    upSeq.addDBRef(pdbDBRef);
+//
+//    for (int x = 1; x < 5; x++)
+//    {
+//      DBRefEntry dbRef = new DBRefEntry();
+//      dbRef.setAccessionId("XYZ_" + x);
+//      seq.addDBRef(dbRef);
+//    }
+//    System.out.println("");
+//    System.out.println(seq.getDBRefs());
+//    System.out.println(query);
+//    query = scquery.buildQuery(seq);
+//    assertEquals(
+//            "uniprot_accession:P12345 OR uniprot_id:P12345 OR pdb_id:1xyz",
+//            query);
+  }
+  @Test(groups= {"Functional"},dataProvider = "testUpSeqs")
+  public void cascadingThreeDBandPDBQuerys(SequenceI testUpSeq)
+  {
+    TDBeaconsFTSRestClientTest.setMock();
+    PDBFTSRestClientTest.setMock();
+    ThreeDBStructureChooserQuerySource tdbquery = new ThreeDBStructureChooserQuerySource();
+    PDBStructureChooserQuerySource pdbquery  = new PDBStructureChooserQuerySource();
+            
+
+    
+    FTSRestResponse upResponse = null;
+    FTSRestResponse pdbResponse = null;
+    // TODO test available options
+    // Best coverage
+    // Best Alphafold Model
+    // Best model (by confidence score)
+    // Will also need to develop a more sophisticated filtering system
+    List<FilterOption> opts = tdbquery.getAvailableFilterOptions(StructureChooser.VIEWS_FILTER);
+    FilterOption opt_singlebest = opts.get(0);
+    FilterOption opt_manybest = opts.get(1);
+    assertEquals(opt_singlebest.getValue(), ThreeDBStructureChooserQuerySource.FILTER_FIRST_BEST_COVERAGE);
+    assertEquals(opt_manybest.getValue(), ThreeDBStructureChooserQuerySource.FILTER_TDBEACONS_COVERAGE);
+    
+    try {
+      upResponse = tdbquery.fetchStructuresMetaData(testUpSeq, tdbquery.getDocFieldPrefs().getStructureSummaryFields(),  opt_singlebest, false);
+      tdbquery.updateAvailableFilterOptions(StructureChooser.VIEWS_FILTER,opts,upResponse.getSearchSummary());
+      // test ranking without additional PDBe data
+      FTSRestResponse firstRanked = tdbquery.selectFirstRankedQuery(testUpSeq, upResponse.getSearchSummary(), tdbquery.getDocFieldPrefs().getStructureSummaryFields(), opt_singlebest.getValue(), false);
+      assertEquals(firstRanked.getNumberOfItemsFound(),1);
+      // many best response
+      upResponse = tdbquery.fetchStructuresMetaData(testUpSeq, tdbquery.getDocFieldPrefs().getStructureSummaryFields(),  opt_manybest, false);
+      assertTrue(firstRanked.getSearchSummary().size()<upResponse.getSearchSummary().size());
+      // NB Could have race condition here 
+      String pdb_Query = tdbquery.buildPDBFTSQueryFor(upResponse);
+      assertTrue(pdb_Query.trim().length()>0);
+      pdbResponse = tdbquery.fetchStructuresMetaDataFor(pdbquery, upResponse);
+      assertTrue(pdbResponse.getNumberOfItemsFound()>0);
+      FTSRestResponse joinedResp = tdbquery.joinResponses(upResponse, pdbResponse);
+      assertEquals(upResponse.getNumberOfItemsFound(),joinedResp.getNumberOfItemsFound());
+      
+      
+    } catch (Exception x)
+    {
+      x.printStackTrace();
+      Assert.fail("Unexpected Exception");
+    }
+    StructureChooserQuerySource scquery = StructureChooserQuerySource.getQuerySourceFor(new SequenceI[] { testUpSeq});
+
+  }
+  
+  @DataProvider(name = "testUpSeqs")
+  public Object[][] testUpSeqs() throws Exception
+  {
+    setUp();
+    return new Object[][] { {upSeq},{upSeq_insulin}};
+  }
+  
+  @Test(groups = { "Functional" })
+  public void sanitizeSeqNameTest()
+  {
+    String name = "ab_cdEF|fwxyz012349";
+    AssertJUnit.assertEquals(name, PDBStructureChooserQuerySource.sanitizeSeqName(name));
+
+    // remove a [nn] substring
+    name = "abcde12[345]fg";
+    AssertJUnit.assertEquals("abcde12fg", PDBStructureChooserQuerySource.sanitizeSeqName(name));
+
+    // remove characters other than a-zA-Z0-9 | or _
+    name = "ab[cd],.\t£$*!- \\\"@:e";
+    AssertJUnit.assertEquals("abcde", PDBStructureChooserQuerySource.sanitizeSeqName(name));
+
+    name = "abcde12[345a]fg";
+    AssertJUnit.assertEquals("abcde12345afg", PDBStructureChooserQuerySource.sanitizeSeqName(name));
+  }
+}
index 05ce22d..9c4be2d 100644 (file)
  */
 package jalview.io;
 
+import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.HiddenColumns;
-import jalview.gui.JvOptionPane;
-import jalview.io.AnnotationFile.ViewDef;
-
+import java.awt.Color;
 import java.io.File;
 import java.util.Hashtable;
+import java.util.List;
 
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.SequenceGroup;
+import jalview.gui.AlignFrame;
+import jalview.gui.JvOptionPane;
+import jalview.io.AnnotationFile.ViewDef;
+
 public class AnnotationFileIOTest
 {
 
@@ -165,4 +170,45 @@ public class AnnotationFileIOTest
             + "\nCouldn't complete Annotation file roundtrip input/output/input test for '"
             + annotFile + "'.");
   }
+  
+  @Test(groups="Functional")
+  public void testAnnotateAlignmentView()
+  {
+    long t1 = System.currentTimeMillis();
+    /*
+     * JAL-3779 test multiple groups of the same name get annotated
+     */
+    AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
+            ">Seq1\nQRSIL\n>Seq2\nFTHND\n>Seq3\nRPVSL\n",
+            DataSourceType.PASTE);
+    long t2 = System.currentTimeMillis();
+    System.err.println("t0: " + (t2 - t1));
+    // seq1 and seq3 are in distinct groups both named Group1
+    String annotationFile = "JALVIEW_ANNOTATION\nSEQUENCE_GROUP\tGroup1\t*\t*\t1\n"
+            + "SEQUENCE_GROUP\tGroup2\t*\t*\t2\n"
+            + "SEQUENCE_GROUP\tGroup1\t*\t*\t3\n"
+            + "PROPERTIES\tGroup1\toutlineColour=blue\tidColour=red\n";
+    new AnnotationFile().annotateAlignmentView(af.getViewport(), annotationFile, DataSourceType.PASTE);
+    
+    AlignmentI al = af.getViewport().getAlignment();
+    List<SequenceGroup> groups = al.getGroups();
+    assertEquals(3, groups.size());
+    SequenceGroup sg = groups.get(0);
+    assertEquals("Group1", sg.getName());
+    assertTrue(sg.contains(al.getSequenceAt(0)));
+    assertEquals(Color.BLUE, sg.getOutlineColour());
+    assertEquals(Color.RED, sg.getIdColour());
+    sg = groups.get(1);
+    assertEquals("Group2", sg.getName());
+    assertTrue(sg.contains(al.getSequenceAt(1)));
+    
+    /*
+     * the bug fix: a second group of the same name is also given properties
+     */
+    sg = groups.get(2);
+    assertEquals("Group1", sg.getName());
+    assertTrue(sg.contains(al.getSequenceAt(2)));
+    assertEquals(Color.BLUE, sg.getOutlineColour());
+    assertEquals(Color.RED, sg.getIdColour());
+  }
 }
diff --git a/test/jalview/io/EmblFlatFileTest.java b/test/jalview/io/EmblFlatFileTest.java
new file mode 100644 (file)
index 0000000..7775c8f
--- /dev/null
@@ -0,0 +1,361 @@
+package jalview.io;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.AssertJUnit.assertNotNull;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertSame;
+import static org.testng.AssertJUnit.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import jalview.bin.Cache;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.Sequence.DBModList;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.SequenceFeatures;
+import jalview.util.MapList;
+
+public class EmblFlatFileTest
+{
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.initLogger();
+  }
+
+  /**
+   * A fairly tough test, using J03321 (circular DNA), which has 8 CDS features,
+   * one of them reverse strand
+   * 
+   * @throws MalformedURLException
+   * @throws IOException
+   */
+  @Test(groups = "Functional")
+  public void testParse() throws MalformedURLException, IOException
+  {
+    File dataFile = new File("test/jalview/io/J03321.embl.txt");
+    FileParse fp = new FileParse(dataFile, DataSourceType.FILE);
+    EmblFlatFile parser = new EmblFlatFile(fp, "EmblTest");
+    List<SequenceI> seqs = parser.getSeqs();
+
+    assertEquals(seqs.size(), 1);
+    SequenceI seq = seqs.get(0);
+    assertEquals(seq.getName(), "EmblTest|J03321");
+    assertEquals(seq.getLength(), 7502);
+    assertEquals(seq.getDescription(),
+            "Chlamydia trachomatis plasmid pCHL1, complete sequence");
+
+    /*
+     * should be 9 CDS features (one is a 'join' of two exons)
+     */
+    Set<String> featureTypes = seq.getFeatures().getFeatureTypes();
+    assertEquals(featureTypes.size(), 1);
+    assertTrue(featureTypes.contains("CDS"));
+
+    /*
+     * inspect some features (sorted just for convenience of test assertions)
+     */
+    List<SequenceFeature> features = seq.getFeatures()
+            .getAllFeatures("CDS");
+    SequenceFeatures.sortFeatures(features, true);
+    assertEquals(features.size(), 9);
+
+    SequenceFeature sf = features.get(0);
+    assertEquals(sf.getBegin(), 1);
+    assertEquals(sf.getEnd(), 437);
+    assertEquals(sf.getDescription(),
+            "Exon 2 for protein EMBLCDS:AAA91567.1");
+    assertEquals(sf.getFeatureGroup(), "EmblTest");
+    assertEquals(sf.getEnaLocation(), "join(7022..7502,1..437)");
+    assertEquals(sf.getPhase(), "0");
+    assertEquals(sf.getStrand(), 1);
+    assertEquals(sf.getValue("note"), "pGP7-D");
+    // this is the second exon of circular CDS!
+    assertEquals(sf.getValue("exon number"), 2);
+    assertEquals(sf.getValue("product"), "hypothetical protein");
+    assertEquals(sf.getValue("transl_table"), "11");
+
+    sf = features.get(1);
+    assertEquals(sf.getBegin(), 488);
+    assertEquals(sf.getEnd(), 1480);
+    assertEquals(sf.getDescription(),
+            "Exon 1 for protein EMBLCDS:AAA91568.1");
+    assertEquals(sf.getFeatureGroup(), "EmblTest");
+    assertEquals(sf.getEnaLocation(), "complement(488..1480)");
+    assertEquals(sf.getPhase(), "0");
+    assertEquals(sf.getStrand(), -1); // reverse strand!
+    assertEquals(sf.getValue("note"), "pGP8-D");
+    assertEquals(sf.getValue("exon number"), 1);
+    assertEquals(sf.getValue("product"), "hypothetical protein");
+
+    sf = features.get(7);
+    assertEquals(sf.getBegin(), 6045);
+    assertEquals(sf.getEnd(), 6788);
+    assertEquals(sf.getDescription(),
+            "Exon 1 for protein EMBLCDS:AAA91574.1");
+    assertEquals(sf.getFeatureGroup(), "EmblTest");
+    assertEquals(sf.getEnaLocation(), "6045..6788");
+    assertEquals(sf.getPhase(), "0");
+    assertEquals(sf.getStrand(), 1);
+    assertEquals(sf.getValue("note"), "pGP6-D (gtg start codon)");
+    assertEquals(sf.getValue("exon number"), 1);
+    assertEquals(sf.getValue("product"), "hypothetical protein");
+
+    /*
+     * CDS at 7022-7502 is the first exon of the circular CDS
+     */
+    sf = features.get(8);
+    assertEquals(sf.getBegin(), 7022);
+    assertEquals(sf.getEnd(), 7502);
+    assertEquals(sf.getDescription(),
+            "Exon 1 for protein EMBLCDS:AAA91567.1");
+    assertEquals(sf.getFeatureGroup(), "EmblTest");
+    assertEquals(sf.getEnaLocation(), "join(7022..7502,1..437)");
+    assertEquals(sf.getPhase(), "0");
+    assertEquals(sf.getStrand(), 1);
+    assertEquals(sf.getValue("note"), "pGP7-D");
+    assertEquals(sf.getValue("exon number"), 1);
+    assertEquals(sf.getValue("product"), "hypothetical protein");
+
+    /*
+     * Verify DBRefs, whether declared in the file or added by Jalview.
+     * There are 4 'direct' (DR) dbrefs, and numerous CDS /db_xref entries 
+     * (some e.g. INTERPRO are duplicates). Jalview adds a dbref to 'self'.
+     * Sample a few here. Note DBRefEntry constructor capitalises source.
+     */
+    List<DBRefEntry> dbrefs = seq.getDBRefs();
+    assertEquals(dbrefs.size(), 32);
+    // xref to 'self':
+    DBRefEntry selfRef = new DBRefEntry("EMBLTEST", "1", "J03321");
+    int[] range = new int[] { 1, seq.getLength() };
+    selfRef.setMap(new Mapping(null, range, range, 1, 1));
+    assertTrue(dbrefs.contains(selfRef));
+
+    // 1st DR line; note trailing period is removed
+    assertTrue(dbrefs.contains(new DBRefEntry("MD5", "0",
+            "d4c4942a634e3df4995fd5ac75c26a61")));
+    // the 4th DR line:
+    assertTrue(
+            dbrefs.contains(new DBRefEntry("EUROPEPMC", "0", "PMC87941")));
+    // from the first CDS feature
+    assertTrue(dbrefs.contains(new DBRefEntry("GOA", "0", "P0CE19")));
+    // from the last CDS feature
+    assertTrue(
+            dbrefs.contains(new DBRefEntry("INTERPRO", "0", "IPR005350")));
+
+    /*
+     * verify mappings to, and sequences for, UNIPROT proteins
+     */
+    int uniprotCount = 0;
+    List<int[]> ranges;
+    for (DBRefEntry dbref : dbrefs)
+    {
+      if ("UNIPROT".equals(dbref.getSource()))
+      {
+        uniprotCount++;
+        Mapping mapping = dbref.getMap();
+        assertNotNull(mapping);
+        MapList map = mapping.getMap();
+        String mappedToName = mapping.getTo().getName();
+        if ("UNIPROT|P0CE16".equals(mappedToName))
+        {
+          assertEquals((ranges = map.getFromRanges()).size(), 1);
+          assertEquals(ranges.get(0)[0], 1579);
+          assertEquals(ranges.get(0)[1], 2934);
+          assertEquals((ranges = map.getToRanges()).size(), 1);
+          assertEquals(ranges.get(0)[0], 1);
+          assertEquals(ranges.get(0)[1], 451);
+          // CDS /product carries over as protein product description
+          assertEquals(mapping.getTo().getDescription(),
+                  "hypothetical protein");
+        }
+        else if ("UNIPROT|P0CE17".equals(mappedToName))
+        {
+          assertEquals((ranges = map.getFromRanges()).size(), 1);
+          assertEquals(ranges.get(0)[0], 2928);
+          assertEquals(ranges.get(0)[1], 3992);
+          assertEquals((ranges = map.getToRanges()).size(), 1);
+          assertEquals(ranges.get(0)[0], 1);
+          assertEquals(ranges.get(0)[1], 354);
+        }
+        else if ("UNIPROT|P0CE18".equals(mappedToName))
+        {
+          assertEquals((ranges = map.getFromRanges()).size(), 1);
+          assertEquals(ranges.get(0)[0], 4054);
+          assertEquals(ranges.get(0)[1], 4848);
+          assertEquals((ranges = map.getToRanges()).size(), 1);
+          assertEquals(ranges.get(0)[0], 1);
+          assertEquals(ranges.get(0)[1], 264);
+        }
+        else if ("UNIPROT|P0CE19".equals(mappedToName))
+        {
+          // join(7022..7502,1..437)
+          assertEquals((ranges = map.getFromRanges()).size(), 2);
+          assertEquals(ranges.get(0)[0], 7022);
+          assertEquals(ranges.get(0)[1], 7502);
+          assertEquals(ranges.get(1)[0], 1);
+          assertEquals(ranges.get(1)[1], 437);
+          assertEquals((ranges = map.getToRanges()).size(), 1);
+          assertEquals(ranges.get(0)[0], 1);
+          assertEquals(ranges.get(0)[1], 305);
+        }
+        else if ("UNIPROT|P0CE20".equals(mappedToName))
+        {
+          // complement(488..1480)
+          assertEquals((ranges = map.getFromRanges()).size(), 1);
+          assertEquals(ranges.get(0)[0], 1480);
+          assertEquals(ranges.get(0)[1], 488);
+          assertEquals((ranges = map.getToRanges()).size(), 1);
+          assertEquals(ranges.get(0)[0], 1);
+          assertEquals(ranges.get(0)[1], 330);
+        }
+        else if (!"UNIPROT|P0CE23".equals(mappedToName)
+                && !"UNIPROT|P10559".equals(mappedToName)
+                && !"UNIPROT|P10560".equals(mappedToName))
+        {
+          fail("Unexpected UNIPROT dbref to " + mappedToName);
+        }
+      }
+    }
+    assertEquals(uniprotCount, 8);
+  }
+  /**
+   * A fairly tough test, using J03321 (circular DNA), which has 8 CDS features,
+   * one of them reverse strand
+   * 
+   * @throws MalformedURLException
+   * @throws IOException
+   */
+  @Test(groups = "Functional")
+  public void testParseToRNA() throws MalformedURLException, IOException
+  {
+    File dataFile = new File("test/jalview/io/J03321_rna.embl.txt");
+    FileParse fp = new FileParse(dataFile, DataSourceType.FILE);
+    EmblFlatFile parser = new EmblFlatFile(fp, "EmblTest");
+    List<SequenceI> seqs = parser.getSeqs();
+    assertTrue(seqs.get(0).getSequenceAsString().indexOf("u")>-1);
+  }
+
+  @Test(groups = "Functional")
+  public void testParse_codonStartNot1()
+  {
+    // TODO verify CDS-to-protein mapping for CDS with /codon_start=2
+    // example: https://www.ebi.ac.uk/ena/browser/api/embl/EU498516
+  }
+
+  /**
+   * Test for the case that the EMBL CDS has no UNIPROT xref. In this case
+   * Jalview should synthesize an xref to EMBLCDSPROTEIN in the hope this will
+   * allow Get Cross-References.
+   * 
+   * @throws IOException
+   */
+  @Test(groups = "Functional")
+  public void testParse_noUniprotXref() throws IOException
+  {
+    // MN908947 cut down to 40BP, one CDS, length 5 peptide for test purposes
+    // plus an additional (invented) test case:
+    // - multi-line /product qualifier including escaped quotes
+    String data = "ID   MN908947; SV 3; linear; genomic RNA; STD; VRL; 20 BP.\n"
+            + "DE   Severe acute respiratory syndrome coronavirus 2 isolate Wuhan-Hu-1,\n"
+            + "FT   CDS             3..17\n"
+            + "FT                   /protein_id=\"QHD43415.1\"\n"
+            + "FT                   /product=\"orf1ab polyprotein\n"
+            + "FT                   \"\"foobar\"\" \"\n"
+            + "FT                   /translation=\"MRKLD\n"
+            + "SQ   Sequence 7496 BP; 2450 A; 1290 C; 1434 G; 2322 T; 0 other;\n"
+            + "     ggatGcgtaa gttagacgaa attttgtctt tgcgcacaga        40\n";
+    FileParse fp = new FileParse(data, DataSourceType.PASTE);
+    EmblFlatFile parser = new EmblFlatFile(fp, "EmblTest");
+    List<SequenceI> seqs = parser.getSeqs();
+    assertEquals(seqs.size(), 1);
+    SequenceI seq = seqs.get(0);
+    DBModList<DBRefEntry> dbrefs = seq.getDBRefs();
+
+    /*
+     * dna should have dbref to itself, and to inferred EMBLCDSPROTEIN:QHD43415.1
+     */
+    assertEquals(dbrefs.size(), 2);
+    
+    // dbref to self
+    DBRefEntry dbref = dbrefs.get(0);
+    assertEquals(dbref.getSource(), "EMBLTEST");
+    assertEquals(dbref.getAccessionId(), "MN908947");
+    Mapping mapping = dbref.getMap();
+    assertNull(mapping.getTo());
+    MapList map = mapping.getMap();
+    assertEquals(map.getFromLowest(), 1);
+    assertEquals(map.getFromHighest(), 40);
+    assertEquals(map.getToLowest(), 1);
+    assertEquals(map.getToHighest(), 40);
+    assertEquals(map.getFromRatio(), 1);
+    assertEquals(map.getToRatio(), 1);
+    
+    // dbref to inferred EMBLCDSPROTEIN:
+    dbref = dbrefs.get(1);
+    assertEquals(dbref.getSource(), "EMBLCDSPROTEIN");
+    assertEquals(dbref.getAccessionId(), "QHD43415.1");
+    mapping = dbref.getMap();
+    SequenceI mapTo = mapping.getTo();
+    assertEquals(mapTo.getName(), "QHD43415.1");
+    // the /product qualifier transfers to protein product description
+    assertEquals(mapTo.getDescription(), "orf1ab polyprotein \"foobar\"");
+    assertEquals(mapTo.getSequenceAsString(), "MRKLD");
+    map = mapping.getMap();
+    assertEquals(map.getFromLowest(), 3);
+    assertEquals(map.getFromHighest(), 17);
+    assertEquals(map.getToLowest(), 1);
+    assertEquals(map.getToHighest(), 5);
+    assertEquals(map.getFromRatio(), 3);
+    assertEquals(map.getToRatio(), 1);
+  }
+
+  @Test(groups = "Functional")
+  public void testAdjustForProteinLength()
+  {
+    int[] exons = new int[] { 11, 15, 21, 25, 31, 38 }; // 18 bp
+
+    // exact length match:
+    assertSame(exons, EmblFlatFile.adjustForProteinLength(6, exons));
+
+    // match if we assume exons include stop codon not in protein:
+    assertSame(exons, EmblFlatFile.adjustForProteinLength(5, exons));
+
+    // truncate last exon by 6bp
+    int[] truncated = EmblFlatFile.adjustForProteinLength(4, exons);
+    assertEquals("[11, 15, 21, 25, 31, 32]", Arrays.toString(truncated));
+
+    // remove last exon and truncate preceding by 1bp (so 3bp in total)
+    truncated = EmblFlatFile.adjustForProteinLength(3, exons);
+    assertEquals("[11, 15, 21, 24]", Arrays.toString(truncated));
+
+    // exact removal of exon case:
+    exons = new int[] { 11, 15, 21, 27, 33, 38 }; // 18 bp
+    truncated = EmblFlatFile.adjustForProteinLength(4, exons);
+    assertEquals("[11, 15, 21, 27]", Arrays.toString(truncated));
+
+    // what if exons are too short for protein?
+    truncated = EmblFlatFile.adjustForProteinLength(7, exons);
+    assertSame(exons, truncated);
+  }
+
+  @Test(groups = "Functional")
+  public void testRemoveQuotes()
+  {
+    assertNull(EmblFlatFile.removeQuotes(null));
+    assertEquals(EmblFlatFile.removeQuotes("No quotes here"), "No quotes here");
+    assertEquals(EmblFlatFile.removeQuotes("\"Enclosing quotes\""), "Enclosing quotes");
+    assertEquals(EmblFlatFile.removeQuotes("\"Escaped \"\"quotes\"\" example\""), "Escaped \"quotes\" example");
+  }
+}
index 53f18bf..3d25bf1 100644 (file)
@@ -1,5 +1,7 @@
 package jalview.io;
 
+import java.util.Locale;
+
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotEquals;
@@ -31,14 +33,15 @@ public class FileFormatsTest
   public void testIsIdentifiable()
   {
     FileFormats formats = FileFormats.getInstance();
-    assertTrue(formats.isIdentifiable(formats.forName(FileFormat.Fasta
-            .getName())));
-    assertTrue(formats.isIdentifiable(formats.forName(FileFormat.MMCif
-            .getName())));
-    assertTrue(formats.isIdentifiable(formats.forName(FileFormat.Jnet
-            .getName())));
-    assertTrue(formats.isIdentifiable(formats.forName(FileFormat.Jalview
-            .getName())));
+    assertTrue(formats
+            .isIdentifiable(formats.forName(FileFormat.Fasta.getName())));
+    assertTrue(formats
+            .isIdentifiable(formats.forName(FileFormat.MMCif.getName())));
+    assertTrue(formats
+            .isIdentifiable(formats.forName(FileFormat.Jnet.getName())));
+    assertTrue(formats
+            .isIdentifiable(formats.forName(FileFormat.Jalview.getName())));
+    // GenBank/ENA
     assertFalse(formats.isIdentifiable(null));
 
     /*
@@ -55,7 +58,7 @@ public class FileFormatsTest
   @Test(groups = "Functional")
   public void testGetReadableFormats()
   {
-    String expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview]";
+    String expected = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GenBank Flatfile, ENA Flatfile, GFF or Jalview features, PDB, mmCIF, Jalview]";
     FileFormats formats = FileFormats.getInstance();
     assertEquals(formats.getReadableFormats().toString(), expected);
   }
@@ -74,14 +77,14 @@ public class FileFormatsTest
   public void testDeregisterFileFormat()
   {
     String writable = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP]";
-    String readable = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview]";
+    String readable = "[Fasta, PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GenBank Flatfile, ENA Flatfile, GFF or Jalview features, PDB, mmCIF, Jalview]";
     FileFormats formats = FileFormats.getInstance();
     assertEquals(formats.getWritableFormats(true).toString(), writable);
     assertEquals(formats.getReadableFormats().toString(), readable);
 
     formats.deregisterFileFormat(FileFormat.Fasta.getName());
     writable = "[PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP]";
-    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview]";
+    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GenBank Flatfile, ENA Flatfile, GFF or Jalview features, PDB, mmCIF, Jalview]";
     assertEquals(formats.getWritableFormats(true).toString(), writable);
     assertEquals(formats.getReadableFormats().toString(), readable);
 
@@ -90,7 +93,7 @@ public class FileFormatsTest
      */
     formats.registerFileFormat(FileFormat.Fasta);
     writable = "[PFAM, Stockholm, PIR, BLC, AMSA, JSON, PileUp, MSF, Clustal, PHYLIP, Fasta]";
-    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GFF or Jalview features, PDB, mmCIF, Jalview, Fasta]";
+    readable = "[PFAM, Stockholm, PIR, BLC, AMSA, HTML, RNAML, JSON, PileUp, MSF, Clustal, PHYLIP, GenBank Flatfile, ENA Flatfile, GFF or Jalview features, PDB, mmCIF, Jalview, Fasta]";
     assertEquals(formats.getWritableFormats(true).toString(), writable);
     assertEquals(formats.getReadableFormats().toString(), readable);
   }
@@ -102,8 +105,8 @@ public class FileFormatsTest
     for (FileFormatI ff : FileFormat.values())
     {
       assertSame(ff, formats.forName(ff.getName()));
-      assertSame(ff, formats.forName(ff.getName().toUpperCase()));
-      assertSame(ff, formats.forName(ff.getName().toLowerCase()));
+      assertSame(ff, formats.forName(ff.getName().toUpperCase(Locale.ROOT)));
+      assertSame(ff, formats.forName(ff.getName().toLowerCase(Locale.ROOT)));
     }
     assertNull(formats.forName(null));
     assertNull(formats.forName("rubbish"));
@@ -144,8 +147,7 @@ public class FileFormatsTest
      * verify the list of file formats registered matches the enum values
      */
     FileFormats instance = FileFormats.getInstance();
-    Iterator<FileFormatI> formats = instance.getFormats()
-            .iterator();
+    Iterator<FileFormatI> formats = instance.getFormats().iterator();
     FileFormatI[] builtIn = FileFormat.values();
 
     for (FileFormatI ff : builtIn)
diff --git a/test/jalview/io/GenBankFileTest.java b/test/jalview/io/GenBankFileTest.java
new file mode 100644 (file)
index 0000000..97e4754
--- /dev/null
@@ -0,0 +1,202 @@
+package jalview.io;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.AssertJUnit.assertNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.List;
+import java.util.Set;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import jalview.bin.Cache;
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.SequenceFeature;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.features.SequenceFeatures;
+import jalview.util.MapList;
+
+public class GenBankFileTest
+{
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.initLogger();
+  }
+
+  /**
+   * A fairly tough test, using J03321 (circular DNA), which has 8 CDS features,
+   * one of them reverse strand
+   * 
+   * @throws MalformedURLException
+   * @throws IOException
+   */
+  @Test(groups = "Functional")
+  public void testParse() throws MalformedURLException, IOException
+  {
+    File dataFile = new File("test/jalview/io/J03321.gb");
+    FileParse fp = new FileParse(dataFile.getAbsolutePath(),
+            DataSourceType.FILE);
+    EMBLLikeFlatFile parser = new GenBankFile(fp, "GenBankTest");
+    List<SequenceI> seqs = parser.getSeqs();
+
+    assertEquals(seqs.size(), 1);
+    SequenceI seq = seqs.get(0);
+    assertEquals(seq.getName(), "GenBankTest|J03321");
+    assertEquals(seq.getLength(), 7502);
+    assertEquals(seq.getDescription(),
+            "Chlamydia trachomatis plasmid pCHL1, complete sequence");
+
+    /*
+     * should be 9 CDS features (one is a 'join' of two exons)
+     */
+    Set<String> featureTypes = seq.getFeatures().getFeatureTypes();
+    assertEquals(featureTypes.size(), 1);
+    assertTrue(featureTypes.contains("CDS"));
+
+    /*
+     * inspect some features (sorted just for convenience of test assertions)
+     */
+    List<SequenceFeature> features = seq.getFeatures()
+            .getAllFeatures("CDS");
+    SequenceFeatures.sortFeatures(features, true);
+    assertEquals(features.size(), 9);
+
+    SequenceFeature sf = features.get(0);
+    assertEquals(sf.getBegin(), 1);
+    assertEquals(sf.getEnd(), 437);
+    assertEquals(sf.getDescription(),
+            "Exon 2 for protein EMBLCDS:AAA91567.1");
+    assertEquals(sf.getFeatureGroup(), "GenBankTest");
+    assertEquals(sf.getEnaLocation(), "join(7022..7502,1..437)");
+    assertEquals(sf.getPhase(), "0");
+    assertEquals(sf.getStrand(), 1);
+    assertEquals(sf.getValue("note"), "pGP7-D");
+    // this is the second exon of circular CDS!
+    assertEquals(sf.getValue("exon number"), 2);
+    assertEquals(sf.getValue("product"), "hypothetical protein");
+    assertEquals(sf.getValue("transl_table"), "11");
+
+    sf = features.get(1);
+    assertEquals(sf.getBegin(), 488);
+    assertEquals(sf.getEnd(), 1480);
+    assertEquals(sf.getDescription(),
+            "Exon 1 for protein EMBLCDS:AAA91568.1");
+    assertEquals(sf.getFeatureGroup(), "GenBankTest");
+    assertEquals(sf.getEnaLocation(), "complement(488..1480)");
+    assertEquals(sf.getPhase(), "0");
+    assertEquals(sf.getStrand(), -1); // reverse strand!
+    assertEquals(sf.getValue("note"), "pGP8-D");
+    assertEquals(sf.getValue("exon number"), 1);
+    assertEquals(sf.getValue("product"), "hypothetical protein");
+
+    sf = features.get(7);
+    assertEquals(sf.getBegin(), 6045);
+    assertEquals(sf.getEnd(), 6788);
+    assertEquals(sf.getDescription(),
+            "Exon 1 for protein EMBLCDS:AAA91574.1");
+    assertEquals(sf.getFeatureGroup(), "GenBankTest");
+    assertEquals(sf.getEnaLocation(), "6045..6788");
+    assertEquals(sf.getPhase(), "0");
+    assertEquals(sf.getStrand(), 1);
+    assertEquals(sf.getValue("note"), "pGP6-D (gtg start codon)");
+    assertEquals(sf.getValue("exon number"), 1);
+    assertEquals(sf.getValue("product"), "hypothetical protein");
+
+    /*
+     * CDS at 7022-7502 is the first exon of the circular CDS
+     */
+    sf = features.get(8);
+    assertEquals(sf.getBegin(), 7022);
+    assertEquals(sf.getEnd(), 7502);
+    assertEquals(sf.getDescription(),
+            "Exon 1 for protein EMBLCDS:AAA91567.1");
+    assertEquals(sf.getFeatureGroup(), "GenBankTest");
+    assertEquals(sf.getEnaLocation(), "join(7022..7502,1..437)");
+    assertEquals(sf.getPhase(), "0");
+    assertEquals(sf.getStrand(), 1);
+    assertEquals(sf.getValue("note"), "pGP7-D");
+    assertEquals(sf.getValue("exon number"), 1);
+    assertEquals(sf.getValue("product"), "hypothetical protein");
+
+    /*
+     * GenBank doesn't declare accession or CDS xrefs;
+     * dbrefs are added by Jalview for 
+     * xref to self : 1
+     * protein products: 8
+     */
+    List<DBRefEntry> dbrefs = seq.getDBRefs();
+
+    assertEquals(dbrefs.size(), 9);
+    // xref to 'self':
+    DBRefEntry selfRef = new DBRefEntry("GENBANKTEST", "1", "J03321");
+    int[] range = new int[] { 1, seq.getLength() };
+    selfRef.setMap(new Mapping(null, range, range, 1, 1));
+    assertTrue(dbrefs.contains(selfRef));
+
+    /*
+     * dna should have dbref to itself, and to EMBLCDSPROTEIN
+     * for each /protein_id (synthesized as no UNIPROT xref)
+     */
+    // TODO check if we should synthesize EMBLCDSPROTEIN dbrefs
+    DBRefEntry dbref = dbrefs.get(0);
+    assertEquals(dbref.getSource(), "GENBANKTEST");
+    assertEquals(dbref.getAccessionId(), "J03321");
+    Mapping mapping = dbref.getMap();
+    assertNull(mapping.getTo());
+    MapList map = mapping.getMap();
+    assertEquals(map.getFromLowest(), 1);
+    assertEquals(map.getFromHighest(), 7502);
+    assertEquals(map.getToLowest(), 1);
+    assertEquals(map.getToHighest(), 7502);
+    assertEquals(map.getFromRatio(), 1);
+    assertEquals(map.getToRatio(), 1);
+
+    // dbref to inferred EMBLCDSPROTEIN for first CDS
+    dbref = dbrefs.get(1);
+    assertEquals(dbref.getSource(), "EMBLCDSPROTEIN");
+    assertEquals(dbref.getAccessionId(), "AAA91567.1");
+    mapping = dbref.getMap();
+    SequenceI mapTo = mapping.getTo();
+    assertEquals(mapTo.getName(), "AAA91567.1");
+    // the /product qualifier transfers to protein product description
+    assertEquals(mapTo.getDescription(), "hypothetical protein");
+    String seqString = mapTo.getSequenceAsString();
+    assertEquals(seqString.length(), 305);
+    assertTrue(seqString.startsWith("MGSMAF"));
+    assertTrue(seqString.endsWith("QTPTIL"));
+    map = mapping.getMap();
+    assertEquals(map.getFromLowest(), 1);
+    assertEquals(map.getFromHighest(), 7502);
+    assertEquals(map.getToLowest(), 1);
+    assertEquals(map.getToHighest(), 305);
+    assertEquals(map.getFromRatio(), 3);
+    assertEquals(map.getToRatio(), 1);
+
+    // dbref to inferred EMBLCDSPROTEIN for last CDS
+    dbref = dbrefs.get(8);
+    assertEquals(dbref.getSource(), "EMBLCDSPROTEIN");
+    assertEquals(dbref.getAccessionId(), "AAA91574.1");
+    mapping = dbref.getMap();
+    mapTo = mapping.getTo();
+    assertEquals(mapTo.getName(), "AAA91574.1");
+    // the /product qualifier transfers to protein product description
+    assertEquals(mapTo.getDescription(), "hypothetical protein");
+    seqString = mapTo.getSequenceAsString();
+    assertEquals(seqString.length(), 247);
+    assertTrue(seqString.startsWith("MNKLK"));
+    assertTrue(seqString.endsWith("FKQKS"));
+    map = mapping.getMap();
+    assertEquals(map.getFromLowest(), 6045);
+    assertEquals(map.getFromHighest(), 6788);
+    assertEquals(map.getToLowest(), 1);
+    assertEquals(map.getToHighest(), 247);
+    assertEquals(map.getFromRatio(), 3);
+    assertEquals(map.getToRatio(), 1);
+  }
+}
index cf7f58f..68c099e 100644 (file)
@@ -24,13 +24,13 @@ import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
-import jalview.gui.JvOptionPane;
-
 import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
+import jalview.gui.JvOptionPane;
+
 public class IdentifyFileTest
 {
 
@@ -102,6 +102,8 @@ public class IdentifyFileTest
         { "examples/testdata/test.html", FileFormat.Html },
         { "examples/testdata/test.pileup", FileFormat.Pileup },
         { "examples/testdata/test.blc", FileFormat.BLC },
+        { "test/jalview/io/J03321.embl.txt", FileFormat.Embl },
+        { "test/jalview/io/J03321.gb", FileFormat.GenBank },
         { "examples/exampleFeatures.txt", FileFormat.Features },
         { "examples/testdata/simpleGff3.gff", FileFormat.Features },
         { "examples/testdata/test.jvp", FileFormat.Jalview },
diff --git a/test/jalview/io/J03321.embl.txt b/test/jalview/io/J03321.embl.txt
new file mode 100644 (file)
index 0000000..555c2b5
--- /dev/null
@@ -0,0 +1,306 @@
+ID   J03321; SV 1; circular; genomic DNA; STD; PRO; 7502 BP.
+XX
+AC   J03321;
+XX
+DT   27-JUL-1990 (Rel. 24, Created)
+DT   10-APR-2020 (Rel. 144, Last updated, Version 9)
+XX
+DE   Chlamydia trachomatis plasmid pCHL1, complete sequence.
+XX
+KW   .
+XX
+OS   Chlamydia trachomatis
+OC   Bacteria; Chlamydiae; Chlamydiales; Chlamydiaceae;
+OC   Chlamydia/Chlamydophila group; Chlamydia.
+OG   Plasmid pCHL1
+XX
+RN   [1]
+RP   1-7502
+RX   DOI; 10.1016/0147-619X(90)90034-A.
+RX   PUBMED; 2194229.
+RA   Comanducci M., Ricci S., Cevenini R., Ratti G.;
+RT   "Diversity of the Chlamydia trachomatis common plasmid in biovars with
+RT   different pathogenicity";
+RL   Plasmid 23(2):149-154(1990).
+XX
+RN   [2]
+RP   1-7502
+RA   Comanducci M., Ricci S., Cevenini R., Ratti G.;
+RT   ;
+RL   Submitted (23-JUN-2010) to the INSDC.
+RL   Sclavo Research Centre, Siena, Italy
+XX
+DR   MD5; d4c4942a634e3df4995fd5ac75c26a61.
+DR   BioSample; SAMN14225621.
+DR   EuropePMC; PMC4450983; 26031715.
+DR   EuropePMC; PMC87941; 11283058.
+XX
+CC   Draft entry and computer-readable sequence kindly submitted by
+CC   G.Ratti, 28-MAR-1990.
+XX
+FH   Key             Location/Qualifiers
+FH
+FT   source          1..7502
+FT                   /organism="Chlamydia trachomatis"
+FT                   /plasmid="pCHL1"
+FT                   /isolate="G0/86"
+FT                   /serotype="D"
+FT                   /mol_type="genomic DNA"
+FT                   /isolation_source="trachoma"
+FT                   /db_xref="taxon:813"
+XX   next line artificially split for test purposes!
+FT   CDS             join(7022..7502,
+FT                   1..437)
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP7-D"
+FT                   /db_xref="GOA:P0CE19"
+FT                   /db_xref="InterPro:IPR002104"
+FT                   /db_xref="InterPro:IPR011010"
+FT                   /db_xref="InterPro:IPR013762"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE19"
+FT                   /protein_id="AAA91567.1"
+FT                   /translation="MGSMAFHKSRLFLTFGDASEIWLSTLSYLTRKNYASGINFLVSLE
+FT                   ILDLSETLIKAISLDHSESLFKIKSLDVFNGKVVSEASKQARAACYISFTKFLYRLTKG
+FT                   YIKPAIPLKDFGNTTFFKIRDKIKTESISKQEWTVFFEALRIVNYRDYLIGKLIVQGIR
+FT                   KLDEILSLRTDDLFFASNQISFRIKKRQNKETKILITFPISLMEELQKYTCGRNGRVFV
+FT                   SKIGIPVTTSQVAHNFRLAEFHSAMKIKITPRVLRASALIHLKQIGLKDEEIMRISCLS
+FT                   SRQSVCSYCSGEEVIPLVQTPTIL"
+FT   CDS             complement(488..1480)
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP8-D"
+FT                   /db_xref="GOA:P0CE20"
+FT                   /db_xref="InterPro:IPR002104"
+FT                   /db_xref="InterPro:IPR011010"
+FT                   /db_xref="InterPro:IPR013762"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE20"
+FT                   /protein_id="AAA91568.1"
+FT                   /translation="MGKGILSLQQEMSLEYSEKSYQEVLKIRQESYWKRMKSFSLFEVI
+FT                   MHWTASLNKHTCRSYRGSFLSLEKIGLLSLDMNLQEFSLLNHNLILDAIKKVSSAKTSW
+FT                   TEGTKQVRAASYISLTRFLNRMTQGIVAIAQPSKQENSRTFFKTREIVKTDAMNSLQTA
+FT                   SFLKELKKINARDWLIAQTMLQGGKRSSEVLSLEISQICFQQATISFSQLKNRQTEKRI
+FT                   IITYPQKFMHFLQEYIGQRRGFVFVTRSGKMVGLRQIARTFSQAGLQAAIPFKITPHVL
+FT                   RATAVTEYKRLGCSDSDIMKVTGHATAKMIFAYDKSSREDNASKKMALI"
+FT   CDS             1579..2934
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP1-D"
+FT                   /db_xref="GOA:P0CE16"
+FT                   /db_xref="InterPro:IPR003593"
+FT                   /db_xref="InterPro:IPR007693"
+FT                   /db_xref="InterPro:IPR007694"
+FT                   /db_xref="InterPro:IPR027417"
+FT                   /db_xref="InterPro:IPR036185"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE16"
+FT                   /protein_id="AAA91569.1"
+FT                   /translation="MKTRSEIENRMQDIEYALLGKALIFEDSTEYILRQLANYEFKCSH
+FT                   HKNIFIVFKHLKDNGLPITVDSAWEELLRRRIKDMDKSYLGLMLHDALSNDKLRSVSHT
+FT                   VFLDDLSVCSAEENLSNFIFRSFNEYNENPLRRSPFLLLERIKGRLDSAIAKTFSIRSA
+FT                   RGRSIYDIFSQSEIGVLARIKKRRVAFSENQNSFFDGFPTGYKDIDDKGVILAKGNFVI
+FT                   IAARPSIGKTALAIDMAINLAVTQQRRVGFLSLEMSAGQIVERIIANLTGISGEKLQRG
+FT                   DLSKEELFRVEEAGETVRESHFYICSDSQYKLNLIANQIRLLRKEDRVDVIFIDYLQLI
+FT                   NSSVGENRQNEIADISRTLRGLASELNIPIVCLSQLSRKVEDRANKVPMLSDLRDSGQI
+FT                   EQDADVILFINRKESSSNCEITVGKNRHGSVFSSVLHFDPKISKFSAIKKVW"
+FT   CDS             2928..3992
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP2-D"
+FT                   /db_xref="InterPro:IPR040719"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE17"
+FT                   /protein_id="AAA91570.1"
+FT                   /translation="MVNYSNCHFIKSPIHLENQKFGRRPGQSIKISPKLAQNGMVEVIG
+FT                   LDFLSSHYHALAAIQRLLTATNYKGNTKGVVLSRESNSFQFEGWIPRIRFTKTEFLEAY
+FT                   GVKRYKTSRNKYEFSGKEAETALEALYHLGHQPFLIVATRTRWTNGTQIVDRYQTLSPI
+FT                   IRIYEGWEGLTDEENIDIDLTPFNSPPTRKHKGFVVEPCPILVDQIESYFVIKPANVYQ
+FT                   EIKMRFPNASKYAYTFIDWVITAAAKKRRKLTKDNSWPENLLLNVNVKSLAYILRMNRY
+FT                   ICTRNWKKIELAIDKCIEIAIQLGWLSRRKRIEFLDSSKLSKKEILYLNKERFEEITKK
+FT                   SKEQMEQLEQESIN"
+FT   CDS             4054..4848
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP3-D"
+FT                   /db_xref="InterPro:IPR008444"
+FT                   /db_xref="InterPro:IPR033758"
+FT                   /db_xref="InterPro:IPR038264"
+FT                   /db_xref="PDB:6GJT"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE18"
+FT                   /protein_id="AAA91571.1"
+FT                   /translation="MGNSGFYLYNTENCVFADNIKVGQMTEPLKDQQIILGTTSTPVAA
+FT                   KMTASDGISLTVSNNSSTNASITIGLDAEKAYQLILEKLGDQILDGIADTIVDSTVQDI
+FT                   LDKIKTDPSLGLLKAFNNFPITNKIQCNGLFTPSNIETLLGGTEIGKFTVTPKSSGSMF
+FT                   LVSADIIASRMEGGVVLALVREGDSKPCAISYGYSSGIPNLCSLRTSITNTGLTPTTYS
+FT                   LRVGGLESGVVWVNALSNGNDILGITNTSNVSFLEVIPQTNA"
+FT   CDS             4918..5226
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP4-D"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE23"
+FT                   /protein_id="AAA91572.1"
+FT                   /translation="MQNKRKVRDDFIKIVKDVKKDFPELDLKIRVNKEKVTFLNSPLEL
+FT                   YHKSVSLILGLLQQIENSLGLFPDSPVLEKLEDNSLKLKKALIMLILSRKDMFSKAE"
+FT   CDS             5317..6048
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP5-D (gtg start codon)"
+FT                   /db_xref="GOA:P10559"
+FT                   /db_xref="InterPro:IPR025669"
+FT                   /db_xref="InterPro:IPR027417"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P10559"
+FT                   /protein_id="AAA91573.1"
+FT                   /translation="MGCNLAQFLGKKVLLADLDPQSNLSSGLGASVRSDQKGLHDIVYT
+FT                   SNDLKSIICETKKDSVDLIPASFSSEQFRELDIHRGPSNNLKLFLNEYCAPFYDICIID
+FT                   TPPSLGGLTKEAFVAGDKLIACLTPEPFSILGLQKIREFLSSVGKPEEEHILGIALSFW
+FT                   DDRNSTNQMYIDIIESIYKNKLFSTKIRRDISLSRSLLKEDSVANVYPNSRAAEDILKL
+FT                   THEIANILHIEYERDYSQRTT"
+FT   CDS             6045..6788
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP6-D (gtg start codon)"
+FT                   /db_xref="InterPro:IPR005350"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P10560"
+FT                   /protein_id="AAA91574.1"
+FT                   /translation="MNKLKKEADVFFKKNQTAASLDFKKTLPSIELFSATLNSEESQSL
+FT                   DRLFLSESQNYSDEEFYQEDILAVKLLTGQIKSIQKQHVLLLGEKIYNARKILSKDHFS
+FT                   STTFSSWIELVFRTKSSAYNALAYYELFINLPNQTLQKEFQSIPYKSAYILAARKGDLK
+FT                   TKVDVIGKVCGMSNSSAIRVLDQFLPSSRNKDVRETIDKSDSEKNRQLSDFLIEILRIM
+FT                   CSGVSLSSYNENLLQQLFELFKQKS"
+FT   repeat_region   6857..6945
+FT                   /note="four tandem 22bp repeats"
+XX
+SQ   Sequence 7502 BP; 2460 A; 1285 C; 1433 G; 2324 T; 0 other;
+     ggatccgtaa gttagacgaa attttgtctt tgcgcacaga cgatctattt tttgcatcca        60
+     atcagatttc ctttcgcatt aaaaaaagac agaataaaga aaccaaaatt ctaatcacat       120
+     ttcctatcag cttaatggaa gagttgcaaa aatacacttg tgggagaaat gggagagtat       180
+     ttgtttctaa aatagggatt cctgtaacaa caagtcaggt tgcgcataat tttaggcttg       240
+     cagagttcca tagtgctatg aaaataaaaa ttactcccag agtacttcgt gcaagcgctt       300
+     tgattcattt aaagcaaata ggattaaaag atgaggaaat catgcgtatt tcctgtcttt       360
+     catcgagaca aagtgtgtgt tcttattgtt ctggggaaga ggtaattcct ctagtacaaa       420
+     cacccacaat attgtgatat aattaaaatt atattcatat tctgttgcca gaaaaaacac       480
+     ctttaggcta tattagagcc atcttctttg aagcgttgtc ttctcgagaa gatttatcgt       540
+     acgcaaatat catctttgcg gttgcgtgtc ctgtgacctt cattatgtcg gagtctgagc       600
+     accctaggcg tttgtactcc gtcacagcgg ttgctcgaag cacgtgcggg gttattttaa       660
+     aagggattgc agcttgtagt cctgcttgag agaacgtgcg ggcgatttgc cttaacccca       720
+     ccatttttcc ggagcgagtt acgaagacaa aacctcttcg ttgaccgatg tactcttgta       780
+     gaaagtgcat aaacttctga ggataagtta taataatcct cttttctgtc tgacggttct       840
+     taagctggga gaaagaaatg gtagcttgtt ggaaacaaat ctgactaatc tccaagctta       900
+     agacttcaga ggagcgttta cctccttgga gcattgtctg ggcgatcaac caatcccggg       960
+     cattgatttt ttttagctct tttaggaagg atgctgtttg caaactgttc atcgcatccg      1020
+     tttttactat ttccctggtt ttaaaaaatg ttcgactatt ttcttgttta gaaggttgcg      1080
+     ctatagcgac tattccttga gtcatcctgt ttaggaatct tgttaaggaa atatagcttg      1140
+     ctgctcgaac ttgtttagta ccttcggtcc aagaagtctt ggcagaggaa acttttttaa      1200
+     tcgcatctag gattagatta tgatttaaaa gggaaaactc ttgcagattc atatccaagg      1260
+     acaatagacc aatcttttct aaagacaaaa aagatcctcg atatgatcta caagtatgtt      1320
+     tgttgagtga tgcggtccaa tgcataataa cttcgaataa ggagaagctt ttcatgcgtt      1380
+     tccaatagga ttcttggcga atttttaaaa cttcctgata agacttttca ctatattcta      1440
+     acgacatttc ttgctgcaaa gataaaatcc ctttacccat gaaatccctc gtgatataac      1500
+     ctatccgtaa aatgtcctga ttagtgaaat aatcaggttg ttaacaggat agcacgctcg      1560
+     gtattttttt atataaacat gaaaactcgt tccgaaatag aaaatcgcat gcaagatatc      1620
+     gagtatgcgt tgttaggtaa agctctgata tttgaagact ctactgagta tattctgagg      1680
+     cagcttgcta attatgagtt taagtgttct catcataaaa acatattcat agtatttaaa      1740
+     cacttaaaag acaatggatt acctataact gtagactcgg cttgggaaga gcttttgcgg      1800
+     cgtcgtatca aagatatgga caaatcgtat ctcgggttaa tgttgcatga tgctttatca      1860
+     aatgacaagc ttagatccgt ttctcatacg gttttcctcg atgatttgag cgtgtgtagc      1920
+     gctgaagaaa atttgagtaa tttcattttc cgctcgttta atgagtacaa tgaaaatcca      1980
+     ttgcgtagat ctccgtttct attgcttgag cgtataaagg gaaggcttga tagtgctata      2040
+     gcaaagactt tttctattcg cagcgctaga ggccggtcta tttatgatat attctcacag      2100
+     tcagaaattg gagtgctggc tcgtataaaa aaaagacgag tagcgttctc tgagaatcaa      2160
+     aattctttct ttgatggctt cccaacagga tacaaggata ttgatgataa aggagttatc      2220
+     ttagctaaag gtaatttcgt gattatagca gctagaccat ctatagggaa aacagcttta      2280
+     gctatagaca tggcgataaa tcttgcggtt actcaacagc gtagagttgg tttcctatct      2340
+     ctagaaatga gcgcaggtca aattgttgag cggattattg ctaatttaac aggaatatct      2400
+     ggtgaaaaat tacaaagagg ggatctctct aaagaagaat tattccgagt agaagaagct      2460
+     ggagaaacgg ttagagaatc acatttttat atctgcagtg atagtcagta taagcttaac      2520
+     ttaatcgcga atcagatccg gttgctgaga aaagaagatc gagtagacgt aatatttatc      2580
+     gattacttgc agttgatcaa ctcatcggtt ggagaaaatc gtcaaaatga aatagcagat      2640
+     atatctagaa ccttaagagg tttagcctca gagctaaaca ttcctatagt ttgtttatcc      2700
+     caactatcta gaaaagttga ggatagagca aataaagttc ccatgctttc agatttgcga      2760
+     gacagcggtc aaatagagca agacgcagat gtgattttgt ttatcaatag gaaggaatcg      2820
+     tcttctaatt gtgagataac tgttgggaaa aatagacatg gatcggtttt ctcttcggta      2880
+     ttacatttcg atccaaaaat tagtaaattc tccgctatta aaaaagtatg gtaaattata      2940
+     gtaactgcca cttcatcaaa agtcctatcc accttgaaaa tcagaagttt ggaagaagac      3000
+     ctggtcaatc tattaagata tctcccaaat tggctcaaaa tgggatggta gaagttatag      3060
+     gtcttgattt tctttcatct cattaccatg cattagcagc tatccaaaga ttactgaccg      3120
+     caacgaatta caaggggaac acaaaagggg ttgttttatc cagagaatca aatagttttc      3180
+     aatttgaagg atggatacca agaatccgtt ttacaaaaac tgaattctta gaggcttatg      3240
+     gagttaagcg gtataaaaca tccagaaata agtatgagtt tagtggaaaa gaagctgaaa      3300
+     ctgctttaga agccttatac catttaggac atcaaccgtt tttaatagtg gcaactagaa      3360
+     ctcgatggac taatggaaca caaatagtag accgttacca aactctttct ccgatcatta      3420
+     ggatttacga aggatgggaa ggtttaactg acgaagaaaa tatagatata gacttaacac      3480
+     cttttaattc accacctaca cggaaacata aagggttcgt tgtagagcca tgtcctatct      3540
+     tggtagatca aatagaatcc tactttgtaa tcaagcctgc aaatgtatac caagaaataa      3600
+     aaatgcgttt cccaaatgca tcaaagtatg cttacacatt tatcgactgg gtgattacag      3660
+     cagctgcgaa aaagagacga aaattaacta aggataattc ttggccagaa aacttgttat      3720
+     taaacgttaa cgttaaaagt cttgcatata ttttaaggat gaatcggtac atctgtacaa      3780
+     ggaactggaa aaaaatcgag ttagctatcg ataaatgtat agaaatcgcc attcagcttg      3840
+     gctggttatc tagaagaaaa cgcattgaat ttctggattc ttctaaactc tctaaaaaag      3900
+     aaattctata tctaaataaa gagcgctttg aagaaataac taagaaatct aaagaacaaa      3960
+     tggaacaatt agaacaagaa tctattaatt aatagcaagc ttgaaactaa aaacctaatt      4020
+     tatttaaagc tcaaaataaa aaagagtttt aaaatgggaa attctggttt ttatttgtat      4080
+     aacactgaaa actgcgtctt tgctgataat atcaaagttg ggcaaatgac agagccgctc      4140
+     aaggaccagc aaataatcct tgggacaaca tcaacacctg tcgcagccaa aatgacagct      4200
+     tctgatggaa tatctttaac agtctccaat aattcatcaa ccaatgcttc tattacaatt      4260
+     ggtttggatg cggaaaaagc ttaccagctt attctagaaa agttgggaga tcaaattctt      4320
+     gatggaattg ctgatactat tgttgatagt acagtccaag atattttaga caaaatcaaa      4380
+     acagaccctt ctctaggttt gttgaaagct tttaacaact ttccaatcac taataaaatt      4440
+     caatgcaacg ggttattcac tcccagtaac attgaaactt tattaggagg aactgaaata      4500
+     ggaaaattca cagtcacacc caaaagctct gggagcatgt tcttagtctc agcagatatt      4560
+     attgcatcaa gaatggaagg cggcgttgtt ctagctttgg tacgagaagg tgattctaag      4620
+     ccctgcgcga ttagttatgg atactcatca ggcattccta atttatgtag tctaagaacc      4680
+     agtattacta atacaggatt gactccgaca acgtattcat tacgtgtagg cggtttagaa      4740
+     agcggtgtgg tatgggttaa tgccctttct aatggcaatg atattttagg aataacaaat      4800
+     acttctaatg tatctttttt agaggtaata cctcaaacaa acgcttaaac aatttttatt      4860
+     ggatttttct tataggtttt atatttagag aaaacagttc gaattacggg gtttgttatg      4920
+     caaaataaaa gaaaagtgag ggacgatttt attaaaattg ttaaagatgt gaaaaaagat      4980
+     ttccccgaat tagacctaaa aatacgagta aacaaggaaa aagtaacttt cttaaattct      5040
+     cccttagaac tctaccataa aagtgtctca ctaattctag gactgcttca acaaatagaa      5100
+     aactctttag gattattccc agactctcct gttcttgaaa aattagagga taacagttta      5160
+     aagctaaaaa aggctttgat tatgcttatc ttgtctagaa aagacatgtt ttccaaggct      5220
+     gaatagacaa cttactctaa cgttggagtt gatttgcaca ccttagtttt ttgctctttt      5280
+     aagggaggaa ctggaaaaac aacactttct ctaaacgtgg gatgcaactt ggcccaattt      5340
+     ttagggaaaa aagtgttact tgctgaccta gacccgcaat ccaatttatc ttctggattg      5400
+     ggggctagtg tcagaagtga ccaaaaaggc ttgcacgaca tagtatacac atcaaacgat      5460
+     ttaaaatcaa tcatttgcga aacaaaaaaa gatagtgtgg acctaattcc tgcatcattt      5520
+     tcatccgaac agtttagaga attggatatt catagaggac ctagtaacaa cttaaagtta      5580
+     tttctgaatg agtactgcgc tcctttttat gacatctgca taatagacac tccacctagc      5640
+     ctaggagggt taacgaaaga agcttttgtt gcaggagaca aattaattgc ttgtttaact      5700
+     ccagaacctt tttctattct agggttacaa aagatacgtg aattcttaag ttcggtcgga      5760
+     aaacctgaag aagaacacat tcttggaata gctttgtctt tttgggatga tcgtaactcg      5820
+     actaaccaaa tgtatataga cattatcgag tctatttaca aaaacaagct tttttcaaca      5880
+     aaaattcgtc gagatatttc tctcagccgt tctcttctta aagaagattc tgtagctaat      5940
+     gtctatccaa attctagggc cgcagaagat attctgaagt taacgcatga aatagcaaat      6000
+     attttgcata tcgaatatga acgagattac tctcagagga caacgtgaac aaactaaaaa      6060
+     aagaagcgga tgtctttttt aaaaaaaatc aaactgccgc ttctctagat tttaagaaga      6120
+     cgcttccctc cattgaacta ttctcagcaa ctttgaattc tgaggaaagt cagagtttgg      6180
+     atcgattatt tttatcagag tcccaaaact attcggatga agaattttat caagaagaca      6240
+     tcctagcggt aaaactgctt actggtcaga taaaatccat acagaagcaa cacgtacttc      6300
+     ttttaggaga aaaaatctat aatgctagaa aaatcctgag taaggatcac ttctcctcaa      6360
+     caactttttc atcttggata gagttagttt ttagaactaa gtcttctgct tacaatgctc      6420
+     ttgcatatta cgagcttttt ataaacctcc ccaaccaaac tctacaaaaa gagtttcaat      6480
+     cgatccccta taaatccgca tatattttgg ccgctagaaa aggcgattta aaaaccaagg      6540
+     tcgatgtgat agggaaagta tgtggaatgt cgaactcatc ggcgataagg gtgttggatc      6600
+     aatttcttcc ttcatctaga aacaaagacg ttagagaaac gatagataag tctgattcag      6660
+     agaagaatcg ccaattatct gatttcttaa tagagatact tcgcatcatg tgttccggag      6720
+     tttctttgtc ctcctataac gaaaatcttc tacaacagct ttttgaactt tttaagcaaa      6780
+     agagctgatc ctccgtcagc tcatatatat atatctatta tatatatata tttagggatt      6840
+     tgatttcacg agagagattt gcaactcttg gtggtagact ttgcaactct tggtggtaga      6900
+     ctttgcaact cttggtggta gactttgcaa ctcttggtgg tagacttggt cataatggac      6960
+     ttttgttaaa aaatttatta aaatcttaga gctccgattt tgaatagctt tggttaagaa      7020
+     aatgggctcg atggctttcc ataaaagtag attgttttta acttttgggg acgcgtcgga      7080
+     aatttggtta tctactttat cttatctaac tagaaaaaat tatgcgtctg ggattaactt      7140
+     tcttgtttct ttagagattc tggatttatc ggaaaccttg ataaaggcta tttctcttga      7200
+     ccacagcgaa tctttgttta aaatcaagtc tctagatgtt tttaatggaa aagttgtttc      7260
+     agaggcatct aaacaggcta gagcggcatg ctacatatct ttcacaaagt ttttgtatag      7320
+     attgaccaag ggatatatta aacccgctat tccattgaaa gattttggaa acactacatt      7380
+     ttttaaaatc cgagacaaaa tcaaaacaga atcgatttct aagcaggaat ggacagtttt      7440
+     ttttgaagcg ctccggatag tgaattatag agactattta atcggtaaat tgattgtaca      7500
+     ag                                                                     7502
+//
diff --git a/test/jalview/io/J03321.gb b/test/jalview/io/J03321.gb
new file mode 100644 (file)
index 0000000..99729e4
--- /dev/null
@@ -0,0 +1,258 @@
+LOCUS       CH1L1CG                 7502 bp    DNA     circular BCT 06-APR-2020
+DEFINITION  Chlamydia trachomatis plasmid pCHL1, complete sequence.
+ACCESSION   J03321
+VERSION     J03321.1
+DBLINK      BioSample: SAMN14225621
+KEYWORDS    .
+SOURCE      Chlamydia trachomatis
+  ORGANISM  Chlamydia trachomatis
+            Bacteria; Chlamydiae; Chlamydiales; Chlamydiaceae;
+            Chlamydia/Chlamydophila group; Chlamydia.
+REFERENCE   1  (bases 1 to 7502)
+  AUTHORS   Comanducci,M., Ricci,S., Cevenini,R. and Ratti,G.
+  TITLE     Diversity of the Chlamydia trachomatis common plasmid in biovars
+            with different pathogenicity
+  JOURNAL   Plasmid 23 (2), 149-154 (1990)
+   PUBMED   2194229
+REFERENCE   2  (bases 1 to 7502)
+  AUTHORS   Comanducci,M., Ricci,S., Cevenini,R. and Ratti,G.
+  TITLE     Direct Submission
+  JOURNAL   Submitted (23-JUN-2010) Sclavo Research Centre, Siena, Italy
+COMMENT     Draft entry and computer-readable sequence kindly submitted by
+            G.Ratti, 28-MAR-1990.
+            ! CDS location split below (and this line added), for Jalview test purposes !
+FEATURES             Location/Qualifiers
+     source          1..7502
+                     /organism="Chlamydia trachomatis"
+                     /mol_type="genomic DNA"
+                     /serotype="D"
+                     /isolate="G0/86"
+                     /isolation_source="trachoma"
+                     /db_xref="taxon:813"
+                     /plasmid="pCHL1"
+     CDS             join(7022..7502,
+                     1..437)
+                     /note="pGP7-D"
+                     /codon_start=1
+                     /transl_table=11
+                     /product="hypothetical protein"
+                     /protein_id="AAA91567.1"
+                     /translation="MGSMAFHKSRLFLTFGDASEIWLSTLSYLTRKNYASGINFLVSL
+                     EILDLSETLIKAISLDHSESLFKIKSLDVFNGKVVSEASKQARAACYISFTKFLYRLT
+                     KGYIKPAIPLKDFGNTTFFKIRDKIKTESISKQEWTVFFEALRIVNYRDYLIGKLIVQ
+                     GIRKLDEILSLRTDDLFFASNQISFRIKKRQNKETKILITFPISLMEELQKYTCGRNG
+                     RVFVSKIGIPVTTSQVAHNFRLAEFHSAMKIKITPRVLRASALIHLKQIGLKDEEIMR
+                     ISCLSSRQSVCSYCSGEEVIPLVQTPTIL"
+     CDS             complement(488..1480)
+                     /note="pGP8-D"
+                     /codon_start=1
+                     /transl_table=11
+                     /product="hypothetical protein"
+                     /protein_id="AAA91568.1"
+                     /translation="MGKGILSLQQEMSLEYSEKSYQEVLKIRQESYWKRMKSFSLFEV
+                     IMHWTASLNKHTCRSYRGSFLSLEKIGLLSLDMNLQEFSLLNHNLILDAIKKVSSAKT
+                     SWTEGTKQVRAASYISLTRFLNRMTQGIVAIAQPSKQENSRTFFKTREIVKTDAMNSL
+                     QTASFLKELKKINARDWLIAQTMLQGGKRSSEVLSLEISQICFQQATISFSQLKNRQT
+                     EKRIIITYPQKFMHFLQEYIGQRRGFVFVTRSGKMVGLRQIARTFSQAGLQAAIPFKI
+                     TPHVLRATAVTEYKRLGCSDSDIMKVTGHATAKMIFAYDKSSREDNASKKMALI"
+     CDS             1579..2934
+                     /note="pGP1-D"
+                     /codon_start=1
+                     /transl_table=11
+                     /product="hypothetical protein"
+                     /protein_id="AAA91569.1"
+                     /translation="MKTRSEIENRMQDIEYALLGKALIFEDSTEYILRQLANYEFKCS
+                     HHKNIFIVFKHLKDNGLPITVDSAWEELLRRRIKDMDKSYLGLMLHDALSNDKLRSVS
+                     HTVFLDDLSVCSAEENLSNFIFRSFNEYNENPLRRSPFLLLERIKGRLDSAIAKTFSI
+                     RSARGRSIYDIFSQSEIGVLARIKKRRVAFSENQNSFFDGFPTGYKDIDDKGVILAKG
+                     NFVIIAARPSIGKTALAIDMAINLAVTQQRRVGFLSLEMSAGQIVERIIANLTGISGE
+                     KLQRGDLSKEELFRVEEAGETVRESHFYICSDSQYKLNLIANQIRLLRKEDRVDVIFI
+                     DYLQLINSSVGENRQNEIADISRTLRGLASELNIPIVCLSQLSRKVEDRANKVPMLSD
+                     LRDSGQIEQDADVILFINRKESSSNCEITVGKNRHGSVFSSVLHFDPKISKFSAIKKV
+                     W"
+     CDS             2928..3992
+                     /note="pGP2-D"
+                     /codon_start=1
+                     /transl_table=11
+                     /product="hypothetical protein"
+                     /protein_id="AAA91570.1"
+                     /translation="MVNYSNCHFIKSPIHLENQKFGRRPGQSIKISPKLAQNGMVEVI
+                     GLDFLSSHYHALAAIQRLLTATNYKGNTKGVVLSRESNSFQFEGWIPRIRFTKTEFLE
+                     AYGVKRYKTSRNKYEFSGKEAETALEALYHLGHQPFLIVATRTRWTNGTQIVDRYQTL
+                     SPIIRIYEGWEGLTDEENIDIDLTPFNSPPTRKHKGFVVEPCPILVDQIESYFVIKPA
+                     NVYQEIKMRFPNASKYAYTFIDWVITAAAKKRRKLTKDNSWPENLLLNVNVKSLAYIL
+                     RMNRYICTRNWKKIELAIDKCIEIAIQLGWLSRRKRIEFLDSSKLSKKEILYLNKERF
+                     EEITKKSKEQMEQLEQESIN"
+     CDS             4054..4848
+                     /note="pGP3-D"
+                     /codon_start=1
+                     /transl_table=11
+                     /product="hypothetical protein"
+                     /protein_id="AAA91571.1"
+                     /translation="MGNSGFYLYNTENCVFADNIKVGQMTEPLKDQQIILGTTSTPVA
+                     AKMTASDGISLTVSNNSSTNASITIGLDAEKAYQLILEKLGDQILDGIADTIVDSTVQ
+                     DILDKIKTDPSLGLLKAFNNFPITNKIQCNGLFTPSNIETLLGGTEIGKFTVTPKSSG
+                     SMFLVSADIIASRMEGGVVLALVREGDSKPCAISYGYSSGIPNLCSLRTSITNTGLTP
+                     TTYSLRVGGLESGVVWVNALSNGNDILGITNTSNVSFLEVIPQTNA"
+     CDS             4918..5226
+                     /note="pGP4-D"
+                     /codon_start=1
+                     /transl_table=11
+                     /product="hypothetical protein"
+                     /protein_id="AAA91572.1"
+                     /translation="MQNKRKVRDDFIKIVKDVKKDFPELDLKIRVNKEKVTFLNSPLE
+                     LYHKSVSLILGLLQQIENSLGLFPDSPVLEKLEDNSLKLKKALIMLILSRKDMFSKAE
+                     "
+     CDS             5317..6048
+                     /note="pGP5-D (gtg start codon)"
+                     /codon_start=1
+                     /transl_table=11
+                     /product="hypothetical protein"
+                     /protein_id="AAA91573.1"
+                     /translation="MGCNLAQFLGKKVLLADLDPQSNLSSGLGASVRSDQKGLHDIVY
+                     TSNDLKSIICETKKDSVDLIPASFSSEQFRELDIHRGPSNNLKLFLNEYCAPFYDICI
+                     IDTPPSLGGLTKEAFVAGDKLIACLTPEPFSILGLQKIREFLSSVGKPEEEHILGIAL
+                     SFWDDRNSTNQMYIDIIESIYKNKLFSTKIRRDISLSRSLLKEDSVANVYPNSRAAED
+                     ILKLTHEIANILHIEYERDYSQRTT"
+     CDS             6045..6788
+                     /note="pGP6-D (gtg start codon)"
+                     /codon_start=1
+                     /transl_table=11
+                     /product="hypothetical protein"
+                     /protein_id="AAA91574.1"
+                     /translation="MNKLKKEADVFFKKNQTAASLDFKKTLPSIELFSATLNSEESQS
+                     LDRLFLSESQNYSDEEFYQEDILAVKLLTGQIKSIQKQHVLLLGEKIYNARKILSKDH
+                     FSSTTFSSWIELVFRTKSSAYNALAYYELFINLPNQTLQKEFQSIPYKSAYILAARKG
+                     DLKTKVDVIGKVCGMSNSSAIRVLDQFLPSSRNKDVRETIDKSDSEKNRQLSDFLIEI
+                     LRIMCSGVSLSSYNENLLQQLFELFKQKS"
+     repeat_region   6857..6945
+                     /note="four tandem 22bp repeats"
+ORIGIN      
+        1 ggatccgtaa gttagacgaa attttgtctt tgcgcacaga cgatctattt tttgcatcca
+       61 atcagatttc ctttcgcatt aaaaaaagac agaataaaga aaccaaaatt ctaatcacat
+      121 ttcctatcag cttaatggaa gagttgcaaa aatacacttg tgggagaaat gggagagtat
+      181 ttgtttctaa aatagggatt cctgtaacaa caagtcaggt tgcgcataat tttaggcttg
+      241 cagagttcca tagtgctatg aaaataaaaa ttactcccag agtacttcgt gcaagcgctt
+      301 tgattcattt aaagcaaata ggattaaaag atgaggaaat catgcgtatt tcctgtcttt
+      361 catcgagaca aagtgtgtgt tcttattgtt ctggggaaga ggtaattcct ctagtacaaa
+      421 cacccacaat attgtgatat aattaaaatt atattcatat tctgttgcca gaaaaaacac
+      481 ctttaggcta tattagagcc atcttctttg aagcgttgtc ttctcgagaa gatttatcgt
+      541 acgcaaatat catctttgcg gttgcgtgtc ctgtgacctt cattatgtcg gagtctgagc
+      601 accctaggcg tttgtactcc gtcacagcgg ttgctcgaag cacgtgcggg gttattttaa
+      661 aagggattgc agcttgtagt cctgcttgag agaacgtgcg ggcgatttgc cttaacccca
+      721 ccatttttcc ggagcgagtt acgaagacaa aacctcttcg ttgaccgatg tactcttgta
+      781 gaaagtgcat aaacttctga ggataagtta taataatcct cttttctgtc tgacggttct
+      841 taagctggga gaaagaaatg gtagcttgtt ggaaacaaat ctgactaatc tccaagctta
+      901 agacttcaga ggagcgttta cctccttgga gcattgtctg ggcgatcaac caatcccggg
+      961 cattgatttt ttttagctct tttaggaagg atgctgtttg caaactgttc atcgcatccg
+     1021 tttttactat ttccctggtt ttaaaaaatg ttcgactatt ttcttgttta gaaggttgcg
+     1081 ctatagcgac tattccttga gtcatcctgt ttaggaatct tgttaaggaa atatagcttg
+     1141 ctgctcgaac ttgtttagta ccttcggtcc aagaagtctt ggcagaggaa acttttttaa
+     1201 tcgcatctag gattagatta tgatttaaaa gggaaaactc ttgcagattc atatccaagg
+     1261 acaatagacc aatcttttct aaagacaaaa aagatcctcg atatgatcta caagtatgtt
+     1321 tgttgagtga tgcggtccaa tgcataataa cttcgaataa ggagaagctt ttcatgcgtt
+     1381 tccaatagga ttcttggcga atttttaaaa cttcctgata agacttttca ctatattcta
+     1441 acgacatttc ttgctgcaaa gataaaatcc ctttacccat gaaatccctc gtgatataac
+     1501 ctatccgtaa aatgtcctga ttagtgaaat aatcaggttg ttaacaggat agcacgctcg
+     1561 gtattttttt atataaacat gaaaactcgt tccgaaatag aaaatcgcat gcaagatatc
+     1621 gagtatgcgt tgttaggtaa agctctgata tttgaagact ctactgagta tattctgagg
+     1681 cagcttgcta attatgagtt taagtgttct catcataaaa acatattcat agtatttaaa
+     1741 cacttaaaag acaatggatt acctataact gtagactcgg cttgggaaga gcttttgcgg
+     1801 cgtcgtatca aagatatgga caaatcgtat ctcgggttaa tgttgcatga tgctttatca
+     1861 aatgacaagc ttagatccgt ttctcatacg gttttcctcg atgatttgag cgtgtgtagc
+     1921 gctgaagaaa atttgagtaa tttcattttc cgctcgttta atgagtacaa tgaaaatcca
+     1981 ttgcgtagat ctccgtttct attgcttgag cgtataaagg gaaggcttga tagtgctata
+     2041 gcaaagactt tttctattcg cagcgctaga ggccggtcta tttatgatat attctcacag
+     2101 tcagaaattg gagtgctggc tcgtataaaa aaaagacgag tagcgttctc tgagaatcaa
+     2161 aattctttct ttgatggctt cccaacagga tacaaggata ttgatgataa aggagttatc
+     2221 ttagctaaag gtaatttcgt gattatagca gctagaccat ctatagggaa aacagcttta
+     2281 gctatagaca tggcgataaa tcttgcggtt actcaacagc gtagagttgg tttcctatct
+     2341 ctagaaatga gcgcaggtca aattgttgag cggattattg ctaatttaac aggaatatct
+     2401 ggtgaaaaat tacaaagagg ggatctctct aaagaagaat tattccgagt agaagaagct
+     2461 ggagaaacgg ttagagaatc acatttttat atctgcagtg atagtcagta taagcttaac
+     2521 ttaatcgcga atcagatccg gttgctgaga aaagaagatc gagtagacgt aatatttatc
+     2581 gattacttgc agttgatcaa ctcatcggtt ggagaaaatc gtcaaaatga aatagcagat
+     2641 atatctagaa ccttaagagg tttagcctca gagctaaaca ttcctatagt ttgtttatcc
+     2701 caactatcta gaaaagttga ggatagagca aataaagttc ccatgctttc agatttgcga
+     2761 gacagcggtc aaatagagca agacgcagat gtgattttgt ttatcaatag gaaggaatcg
+     2821 tcttctaatt gtgagataac tgttgggaaa aatagacatg gatcggtttt ctcttcggta
+     2881 ttacatttcg atccaaaaat tagtaaattc tccgctatta aaaaagtatg gtaaattata
+     2941 gtaactgcca cttcatcaaa agtcctatcc accttgaaaa tcagaagttt ggaagaagac
+     3001 ctggtcaatc tattaagata tctcccaaat tggctcaaaa tgggatggta gaagttatag
+     3061 gtcttgattt tctttcatct cattaccatg cattagcagc tatccaaaga ttactgaccg
+     3121 caacgaatta caaggggaac acaaaagggg ttgttttatc cagagaatca aatagttttc
+     3181 aatttgaagg atggatacca agaatccgtt ttacaaaaac tgaattctta gaggcttatg
+     3241 gagttaagcg gtataaaaca tccagaaata agtatgagtt tagtggaaaa gaagctgaaa
+     3301 ctgctttaga agccttatac catttaggac atcaaccgtt tttaatagtg gcaactagaa
+     3361 ctcgatggac taatggaaca caaatagtag accgttacca aactctttct ccgatcatta
+     3421 ggatttacga aggatgggaa ggtttaactg acgaagaaaa tatagatata gacttaacac
+     3481 cttttaattc accacctaca cggaaacata aagggttcgt tgtagagcca tgtcctatct
+     3541 tggtagatca aatagaatcc tactttgtaa tcaagcctgc aaatgtatac caagaaataa
+     3601 aaatgcgttt cccaaatgca tcaaagtatg cttacacatt tatcgactgg gtgattacag
+     3661 cagctgcgaa aaagagacga aaattaacta aggataattc ttggccagaa aacttgttat
+     3721 taaacgttaa cgttaaaagt cttgcatata ttttaaggat gaatcggtac atctgtacaa
+     3781 ggaactggaa aaaaatcgag ttagctatcg ataaatgtat agaaatcgcc attcagcttg
+     3841 gctggttatc tagaagaaaa cgcattgaat ttctggattc ttctaaactc tctaaaaaag
+     3901 aaattctata tctaaataaa gagcgctttg aagaaataac taagaaatct aaagaacaaa
+     3961 tggaacaatt agaacaagaa tctattaatt aatagcaagc ttgaaactaa aaacctaatt
+     4021 tatttaaagc tcaaaataaa aaagagtttt aaaatgggaa attctggttt ttatttgtat
+     4081 aacactgaaa actgcgtctt tgctgataat atcaaagttg ggcaaatgac agagccgctc
+     4141 aaggaccagc aaataatcct tgggacaaca tcaacacctg tcgcagccaa aatgacagct
+     4201 tctgatggaa tatctttaac agtctccaat aattcatcaa ccaatgcttc tattacaatt
+     4261 ggtttggatg cggaaaaagc ttaccagctt attctagaaa agttgggaga tcaaattctt
+     4321 gatggaattg ctgatactat tgttgatagt acagtccaag atattttaga caaaatcaaa
+     4381 acagaccctt ctctaggttt gttgaaagct tttaacaact ttccaatcac taataaaatt
+     4441 caatgcaacg ggttattcac tcccagtaac attgaaactt tattaggagg aactgaaata
+     4501 ggaaaattca cagtcacacc caaaagctct gggagcatgt tcttagtctc agcagatatt
+     4561 attgcatcaa gaatggaagg cggcgttgtt ctagctttgg tacgagaagg tgattctaag
+     4621 ccctgcgcga ttagttatgg atactcatca ggcattccta atttatgtag tctaagaacc
+     4681 agtattacta atacaggatt gactccgaca acgtattcat tacgtgtagg cggtttagaa
+     4741 agcggtgtgg tatgggttaa tgccctttct aatggcaatg atattttagg aataacaaat
+     4801 acttctaatg tatctttttt agaggtaata cctcaaacaa acgcttaaac aatttttatt
+     4861 ggatttttct tataggtttt atatttagag aaaacagttc gaattacggg gtttgttatg
+     4921 caaaataaaa gaaaagtgag ggacgatttt attaaaattg ttaaagatgt gaaaaaagat
+     4981 ttccccgaat tagacctaaa aatacgagta aacaaggaaa aagtaacttt cttaaattct
+     5041 cccttagaac tctaccataa aagtgtctca ctaattctag gactgcttca acaaatagaa
+     5101 aactctttag gattattccc agactctcct gttcttgaaa aattagagga taacagttta
+     5161 aagctaaaaa aggctttgat tatgcttatc ttgtctagaa aagacatgtt ttccaaggct
+     5221 gaatagacaa cttactctaa cgttggagtt gatttgcaca ccttagtttt ttgctctttt
+     5281 aagggaggaa ctggaaaaac aacactttct ctaaacgtgg gatgcaactt ggcccaattt
+     5341 ttagggaaaa aagtgttact tgctgaccta gacccgcaat ccaatttatc ttctggattg
+     5401 ggggctagtg tcagaagtga ccaaaaaggc ttgcacgaca tagtatacac atcaaacgat
+     5461 ttaaaatcaa tcatttgcga aacaaaaaaa gatagtgtgg acctaattcc tgcatcattt
+     5521 tcatccgaac agtttagaga attggatatt catagaggac ctagtaacaa cttaaagtta
+     5581 tttctgaatg agtactgcgc tcctttttat gacatctgca taatagacac tccacctagc
+     5641 ctaggagggt taacgaaaga agcttttgtt gcaggagaca aattaattgc ttgtttaact
+     5701 ccagaacctt tttctattct agggttacaa aagatacgtg aattcttaag ttcggtcgga
+     5761 aaacctgaag aagaacacat tcttggaata gctttgtctt tttgggatga tcgtaactcg
+     5821 actaaccaaa tgtatataga cattatcgag tctatttaca aaaacaagct tttttcaaca
+     5881 aaaattcgtc gagatatttc tctcagccgt tctcttctta aagaagattc tgtagctaat
+     5941 gtctatccaa attctagggc cgcagaagat attctgaagt taacgcatga aatagcaaat
+     6001 attttgcata tcgaatatga acgagattac tctcagagga caacgtgaac aaactaaaaa
+     6061 aagaagcgga tgtctttttt aaaaaaaatc aaactgccgc ttctctagat tttaagaaga
+     6121 cgcttccctc cattgaacta ttctcagcaa ctttgaattc tgaggaaagt cagagtttgg
+     6181 atcgattatt tttatcagag tcccaaaact attcggatga agaattttat caagaagaca
+     6241 tcctagcggt aaaactgctt actggtcaga taaaatccat acagaagcaa cacgtacttc
+     6301 ttttaggaga aaaaatctat aatgctagaa aaatcctgag taaggatcac ttctcctcaa
+     6361 caactttttc atcttggata gagttagttt ttagaactaa gtcttctgct tacaatgctc
+     6421 ttgcatatta cgagcttttt ataaacctcc ccaaccaaac tctacaaaaa gagtttcaat
+     6481 cgatccccta taaatccgca tatattttgg ccgctagaaa aggcgattta aaaaccaagg
+     6541 tcgatgtgat agggaaagta tgtggaatgt cgaactcatc ggcgataagg gtgttggatc
+     6601 aatttcttcc ttcatctaga aacaaagacg ttagagaaac gatagataag tctgattcag
+     6661 agaagaatcg ccaattatct gatttcttaa tagagatact tcgcatcatg tgttccggag
+     6721 tttctttgtc ctcctataac gaaaatcttc tacaacagct ttttgaactt tttaagcaaa
+     6781 agagctgatc ctccgtcagc tcatatatat atatctatta tatatatata tttagggatt
+     6841 tgatttcacg agagagattt gcaactcttg gtggtagact ttgcaactct tggtggtaga
+     6901 ctttgcaact cttggtggta gactttgcaa ctcttggtgg tagacttggt cataatggac
+     6961 ttttgttaaa aaatttatta aaatcttaga gctccgattt tgaatagctt tggttaagaa
+     7021 aatgggctcg atggctttcc ataaaagtag attgttttta acttttgggg acgcgtcgga
+     7081 aatttggtta tctactttat cttatctaac tagaaaaaat tatgcgtctg ggattaactt
+     7141 tcttgtttct ttagagattc tggatttatc ggaaaccttg ataaaggcta tttctcttga
+     7201 ccacagcgaa tctttgttta aaatcaagtc tctagatgtt tttaatggaa aagttgtttc
+     7261 agaggcatct aaacaggcta gagcggcatg ctacatatct ttcacaaagt ttttgtatag
+     7321 attgaccaag ggatatatta aacccgctat tccattgaaa gattttggaa acactacatt
+     7381 ttttaaaatc cgagacaaaa tcaaaacaga atcgatttct aagcaggaat ggacagtttt
+     7441 ttttgaagcg ctccggatag tgaattatag agactattta atcggtaaat tgattgtaca
+     7501 ag
+//
+
diff --git a/test/jalview/io/J03321_rna.embl.txt b/test/jalview/io/J03321_rna.embl.txt
new file mode 100644 (file)
index 0000000..84b9b69
--- /dev/null
@@ -0,0 +1,306 @@
+ID   J03321; SV 1; circular; genomic DNA; STD; PRO; 7502 BP.
+XX
+AC   J03321;
+XX
+DT   27-JUL-1990 (Rel. 24, Created)
+DT   10-APR-2020 (Rel. 144, Last updated, Version 9)
+XX
+DE   Chlamydia trachomatis plasmid pCHL1, complete sequence.
+XX
+KW   .
+XX
+OS   Chlamydia trachomatis
+OC   Bacteria; Chlamydiae; Chlamydiales; Chlamydiaceae;
+OC   Chlamydia/Chlamydophila group; Chlamydia.
+OG   Plasmid pCHL1
+XX
+RN   [1]
+RP   1-7502
+RX   DOI; 10.1016/0147-619X(90)90034-A.
+RX   PUBMED; 2194229.
+RA   Comanducci M., Ricci S., Cevenini R., Ratti G.;
+RT   "Diversity of the Chlamydia trachomatis common plasmid in biovars with
+RT   different pathogenicity";
+RL   Plasmid 23(2):149-154(1990).
+XX
+RN   [2]
+RP   1-7502
+RA   Comanducci M., Ricci S., Cevenini R., Ratti G.;
+RT   ;
+RL   Submitted (23-JUN-2010) to the INSDC.
+RL   Sclavo Research Centre, Siena, Italy
+XX
+DR   MD5; d4c4942a634e3df4995fd5ac75c26a61.
+DR   BioSample; SAMN14225621.
+DR   EuropePMC; PMC4450983; 26031715.
+DR   EuropePMC; PMC87941; 11283058.
+XX
+CC   Draft entry and computer-readable sequence kindly submitted by
+CC   G.Ratti, 28-MAR-1990.
+XX
+FH   Key             Location/Qualifiers
+FH
+FT   source          1..7502
+FT                   /organism="Chlamydia trachomatis"
+FT                   /plasmid="pCHL1"
+FT                   /isolate="G0/86"
+FT                   /serotype="D"
+FT                   /mol_type="genomic RNA"
+FT                   /isolation_source="trachoma"
+FT                   /db_xref="taxon:813"
+XX   next line artificially split for test purposes!
+FT   CDS             join(7022..7502,
+FT                   1..437)
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP7-D"
+FT                   /db_xref="GOA:P0CE19"
+FT                   /db_xref="InterPro:IPR002104"
+FT                   /db_xref="InterPro:IPR011010"
+FT                   /db_xref="InterPro:IPR013762"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE19"
+FT                   /protein_id="AAA91567.1"
+FT                   /translation="MGSMAFHKSRLFLTFGDASEIWLSTLSYLTRKNYASGINFLVSLE
+FT                   ILDLSETLIKAISLDHSESLFKIKSLDVFNGKVVSEASKQARAACYISFTKFLYRLTKG
+FT                   YIKPAIPLKDFGNTTFFKIRDKIKTESISKQEWTVFFEALRIVNYRDYLIGKLIVQGIR
+FT                   KLDEILSLRTDDLFFASNQISFRIKKRQNKETKILITFPISLMEELQKYTCGRNGRVFV
+FT                   SKIGIPVTTSQVAHNFRLAEFHSAMKIKITPRVLRASALIHLKQIGLKDEEIMRISCLS
+FT                   SRQSVCSYCSGEEVIPLVQTPTIL"
+FT   CDS             complement(488..1480)
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP8-D"
+FT                   /db_xref="GOA:P0CE20"
+FT                   /db_xref="InterPro:IPR002104"
+FT                   /db_xref="InterPro:IPR011010"
+FT                   /db_xref="InterPro:IPR013762"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE20"
+FT                   /protein_id="AAA91568.1"
+FT                   /translation="MGKGILSLQQEMSLEYSEKSYQEVLKIRQESYWKRMKSFSLFEVI
+FT                   MHWTASLNKHTCRSYRGSFLSLEKIGLLSLDMNLQEFSLLNHNLILDAIKKVSSAKTSW
+FT                   TEGTKQVRAASYISLTRFLNRMTQGIVAIAQPSKQENSRTFFKTREIVKTDAMNSLQTA
+FT                   SFLKELKKINARDWLIAQTMLQGGKRSSEVLSLEISQICFQQATISFSQLKNRQTEKRI
+FT                   IITYPQKFMHFLQEYIGQRRGFVFVTRSGKMVGLRQIARTFSQAGLQAAIPFKITPHVL
+FT                   RATAVTEYKRLGCSDSDIMKVTGHATAKMIFAYDKSSREDNASKKMALI"
+FT   CDS             1579..2934
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP1-D"
+FT                   /db_xref="GOA:P0CE16"
+FT                   /db_xref="InterPro:IPR003593"
+FT                   /db_xref="InterPro:IPR007693"
+FT                   /db_xref="InterPro:IPR007694"
+FT                   /db_xref="InterPro:IPR027417"
+FT                   /db_xref="InterPro:IPR036185"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE16"
+FT                   /protein_id="AAA91569.1"
+FT                   /translation="MKTRSEIENRMQDIEYALLGKALIFEDSTEYILRQLANYEFKCSH
+FT                   HKNIFIVFKHLKDNGLPITVDSAWEELLRRRIKDMDKSYLGLMLHDALSNDKLRSVSHT
+FT                   VFLDDLSVCSAEENLSNFIFRSFNEYNENPLRRSPFLLLERIKGRLDSAIAKTFSIRSA
+FT                   RGRSIYDIFSQSEIGVLARIKKRRVAFSENQNSFFDGFPTGYKDIDDKGVILAKGNFVI
+FT                   IAARPSIGKTALAIDMAINLAVTQQRRVGFLSLEMSAGQIVERIIANLTGISGEKLQRG
+FT                   DLSKEELFRVEEAGETVRESHFYICSDSQYKLNLIANQIRLLRKEDRVDVIFIDYLQLI
+FT                   NSSVGENRQNEIADISRTLRGLASELNIPIVCLSQLSRKVEDRANKVPMLSDLRDSGQI
+FT                   EQDADVILFINRKESSSNCEITVGKNRHGSVFSSVLHFDPKISKFSAIKKVW"
+FT   CDS             2928..3992
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP2-D"
+FT                   /db_xref="InterPro:IPR040719"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE17"
+FT                   /protein_id="AAA91570.1"
+FT                   /translation="MVNYSNCHFIKSPIHLENQKFGRRPGQSIKISPKLAQNGMVEVIG
+FT                   LDFLSSHYHALAAIQRLLTATNYKGNTKGVVLSRESNSFQFEGWIPRIRFTKTEFLEAY
+FT                   GVKRYKTSRNKYEFSGKEAETALEALYHLGHQPFLIVATRTRWTNGTQIVDRYQTLSPI
+FT                   IRIYEGWEGLTDEENIDIDLTPFNSPPTRKHKGFVVEPCPILVDQIESYFVIKPANVYQ
+FT                   EIKMRFPNASKYAYTFIDWVITAAAKKRRKLTKDNSWPENLLLNVNVKSLAYILRMNRY
+FT                   ICTRNWKKIELAIDKCIEIAIQLGWLSRRKRIEFLDSSKLSKKEILYLNKERFEEITKK
+FT                   SKEQMEQLEQESIN"
+FT   CDS             4054..4848
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP3-D"
+FT                   /db_xref="InterPro:IPR008444"
+FT                   /db_xref="InterPro:IPR033758"
+FT                   /db_xref="InterPro:IPR038264"
+FT                   /db_xref="PDB:6GJT"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE18"
+FT                   /protein_id="AAA91571.1"
+FT                   /translation="MGNSGFYLYNTENCVFADNIKVGQMTEPLKDQQIILGTTSTPVAA
+FT                   KMTASDGISLTVSNNSSTNASITIGLDAEKAYQLILEKLGDQILDGIADTIVDSTVQDI
+FT                   LDKIKTDPSLGLLKAFNNFPITNKIQCNGLFTPSNIETLLGGTEIGKFTVTPKSSGSMF
+FT                   LVSADIIASRMEGGVVLALVREGDSKPCAISYGYSSGIPNLCSLRTSITNTGLTPTTYS
+FT                   LRVGGLESGVVWVNALSNGNDILGITNTSNVSFLEVIPQTNA"
+FT   CDS             4918..5226
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP4-D"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P0CE23"
+FT                   /protein_id="AAA91572.1"
+FT                   /translation="MQNKRKVRDDFIKIVKDVKKDFPELDLKIRVNKEKVTFLNSPLEL
+FT                   YHKSVSLILGLLQQIENSLGLFPDSPVLEKLEDNSLKLKKALIMLILSRKDMFSKAE"
+FT   CDS             5317..6048
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP5-D (gtg start codon)"
+FT                   /db_xref="GOA:P10559"
+FT                   /db_xref="InterPro:IPR025669"
+FT                   /db_xref="InterPro:IPR027417"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P10559"
+FT                   /protein_id="AAA91573.1"
+FT                   /translation="MGCNLAQFLGKKVLLADLDPQSNLSSGLGASVRSDQKGLHDIVYT
+FT                   SNDLKSIICETKKDSVDLIPASFSSEQFRELDIHRGPSNNLKLFLNEYCAPFYDICIID
+FT                   TPPSLGGLTKEAFVAGDKLIACLTPEPFSILGLQKIREFLSSVGKPEEEHILGIALSFW
+FT                   DDRNSTNQMYIDIIESIYKNKLFSTKIRRDISLSRSLLKEDSVANVYPNSRAAEDILKL
+FT                   THEIANILHIEYERDYSQRTT"
+FT   CDS             6045..6788
+FT                   /codon_start=1
+FT                   /transl_table=11
+FT                   /product="hypothetical protein"
+FT                   /note="pGP6-D (gtg start codon)"
+FT                   /db_xref="InterPro:IPR005350"
+FT                   /db_xref="UniProtKB/Swiss-Prot:P10560"
+FT                   /protein_id="AAA91574.1"
+FT                   /translation="MNKLKKEADVFFKKNQTAASLDFKKTLPSIELFSATLNSEESQSL
+FT                   DRLFLSESQNYSDEEFYQEDILAVKLLTGQIKSIQKQHVLLLGEKIYNARKILSKDHFS
+FT                   STTFSSWIELVFRTKSSAYNALAYYELFINLPNQTLQKEFQSIPYKSAYILAARKGDLK
+FT                   TKVDVIGKVCGMSNSSAIRVLDQFLPSSRNKDVRETIDKSDSEKNRQLSDFLIEILRIM
+FT                   CSGVSLSSYNENLLQQLFELFKQKS"
+FT   repeat_region   6857..6945
+FT                   /note="four tandem 22bp repeats"
+XX
+SQ   Sequence 7502 BP; 2460 A; 1285 C; 1433 G; 2324 T; 0 other;
+     ggatccgtaa gttagacgaa attttgtctt tgcgcacaga cgatctattt tttgcatcca        60
+     atcagatttc ctttcgcatt aaaaaaagac agaataaaga aaccaaaatt ctaatcacat       120
+     ttcctatcag cttaatggaa gagttgcaaa aatacacttg tgggagaaat gggagagtat       180
+     ttgtttctaa aatagggatt cctgtaacaa caagtcaggt tgcgcataat tttaggcttg       240
+     cagagttcca tagtgctatg aaaataaaaa ttactcccag agtacttcgt gcaagcgctt       300
+     tgattcattt aaagcaaata ggattaaaag atgaggaaat catgcgtatt tcctgtcttt       360
+     catcgagaca aagtgtgtgt tcttattgtt ctggggaaga ggtaattcct ctagtacaaa       420
+     cacccacaat attgtgatat aattaaaatt atattcatat tctgttgcca gaaaaaacac       480
+     ctttaggcta tattagagcc atcttctttg aagcgttgtc ttctcgagaa gatttatcgt       540
+     acgcaaatat catctttgcg gttgcgtgtc ctgtgacctt cattatgtcg gagtctgagc       600
+     accctaggcg tttgtactcc gtcacagcgg ttgctcgaag cacgtgcggg gttattttaa       660
+     aagggattgc agcttgtagt cctgcttgag agaacgtgcg ggcgatttgc cttaacccca       720
+     ccatttttcc ggagcgagtt acgaagacaa aacctcttcg ttgaccgatg tactcttgta       780
+     gaaagtgcat aaacttctga ggataagtta taataatcct cttttctgtc tgacggttct       840
+     taagctggga gaaagaaatg gtagcttgtt ggaaacaaat ctgactaatc tccaagctta       900
+     agacttcaga ggagcgttta cctccttgga gcattgtctg ggcgatcaac caatcccggg       960
+     cattgatttt ttttagctct tttaggaagg atgctgtttg caaactgttc atcgcatccg      1020
+     tttttactat ttccctggtt ttaaaaaatg ttcgactatt ttcttgttta gaaggttgcg      1080
+     ctatagcgac tattccttga gtcatcctgt ttaggaatct tgttaaggaa atatagcttg      1140
+     ctgctcgaac ttgtttagta ccttcggtcc aagaagtctt ggcagaggaa acttttttaa      1200
+     tcgcatctag gattagatta tgatttaaaa gggaaaactc ttgcagattc atatccaagg      1260
+     acaatagacc aatcttttct aaagacaaaa aagatcctcg atatgatcta caagtatgtt      1320
+     tgttgagtga tgcggtccaa tgcataataa cttcgaataa ggagaagctt ttcatgcgtt      1380
+     tccaatagga ttcttggcga atttttaaaa cttcctgata agacttttca ctatattcta      1440
+     acgacatttc ttgctgcaaa gataaaatcc ctttacccat gaaatccctc gtgatataac      1500
+     ctatccgtaa aatgtcctga ttagtgaaat aatcaggttg ttaacaggat agcacgctcg      1560
+     gtattttttt atataaacat gaaaactcgt tccgaaatag aaaatcgcat gcaagatatc      1620
+     gagtatgcgt tgttaggtaa agctctgata tttgaagact ctactgagta tattctgagg      1680
+     cagcttgcta attatgagtt taagtgttct catcataaaa acatattcat agtatttaaa      1740
+     cacttaaaag acaatggatt acctataact gtagactcgg cttgggaaga gcttttgcgg      1800
+     cgtcgtatca aagatatgga caaatcgtat ctcgggttaa tgttgcatga tgctttatca      1860
+     aatgacaagc ttagatccgt ttctcatacg gttttcctcg atgatttgag cgtgtgtagc      1920
+     gctgaagaaa atttgagtaa tttcattttc cgctcgttta atgagtacaa tgaaaatcca      1980
+     ttgcgtagat ctccgtttct attgcttgag cgtataaagg gaaggcttga tagtgctata      2040
+     gcaaagactt tttctattcg cagcgctaga ggccggtcta tttatgatat attctcacag      2100
+     tcagaaattg gagtgctggc tcgtataaaa aaaagacgag tagcgttctc tgagaatcaa      2160
+     aattctttct ttgatggctt cccaacagga tacaaggata ttgatgataa aggagttatc      2220
+     ttagctaaag gtaatttcgt gattatagca gctagaccat ctatagggaa aacagcttta      2280
+     gctatagaca tggcgataaa tcttgcggtt actcaacagc gtagagttgg tttcctatct      2340
+     ctagaaatga gcgcaggtca aattgttgag cggattattg ctaatttaac aggaatatct      2400
+     ggtgaaaaat tacaaagagg ggatctctct aaagaagaat tattccgagt agaagaagct      2460
+     ggagaaacgg ttagagaatc acatttttat atctgcagtg atagtcagta taagcttaac      2520
+     ttaatcgcga atcagatccg gttgctgaga aaagaagatc gagtagacgt aatatttatc      2580
+     gattacttgc agttgatcaa ctcatcggtt ggagaaaatc gtcaaaatga aatagcagat      2640
+     atatctagaa ccttaagagg tttagcctca gagctaaaca ttcctatagt ttgtttatcc      2700
+     caactatcta gaaaagttga ggatagagca aataaagttc ccatgctttc agatttgcga      2760
+     gacagcggtc aaatagagca agacgcagat gtgattttgt ttatcaatag gaaggaatcg      2820
+     tcttctaatt gtgagataac tgttgggaaa aatagacatg gatcggtttt ctcttcggta      2880
+     ttacatttcg atccaaaaat tagtaaattc tccgctatta aaaaagtatg gtaaattata      2940
+     gtaactgcca cttcatcaaa agtcctatcc accttgaaaa tcagaagttt ggaagaagac      3000
+     ctggtcaatc tattaagata tctcccaaat tggctcaaaa tgggatggta gaagttatag      3060
+     gtcttgattt tctttcatct cattaccatg cattagcagc tatccaaaga ttactgaccg      3120
+     caacgaatta caaggggaac acaaaagggg ttgttttatc cagagaatca aatagttttc      3180
+     aatttgaagg atggatacca agaatccgtt ttacaaaaac tgaattctta gaggcttatg      3240
+     gagttaagcg gtataaaaca tccagaaata agtatgagtt tagtggaaaa gaagctgaaa      3300
+     ctgctttaga agccttatac catttaggac atcaaccgtt tttaatagtg gcaactagaa      3360
+     ctcgatggac taatggaaca caaatagtag accgttacca aactctttct ccgatcatta      3420
+     ggatttacga aggatgggaa ggtttaactg acgaagaaaa tatagatata gacttaacac      3480
+     cttttaattc accacctaca cggaaacata aagggttcgt tgtagagcca tgtcctatct      3540
+     tggtagatca aatagaatcc tactttgtaa tcaagcctgc aaatgtatac caagaaataa      3600
+     aaatgcgttt cccaaatgca tcaaagtatg cttacacatt tatcgactgg gtgattacag      3660
+     cagctgcgaa aaagagacga aaattaacta aggataattc ttggccagaa aacttgttat      3720
+     taaacgttaa cgttaaaagt cttgcatata ttttaaggat gaatcggtac atctgtacaa      3780
+     ggaactggaa aaaaatcgag ttagctatcg ataaatgtat agaaatcgcc attcagcttg      3840
+     gctggttatc tagaagaaaa cgcattgaat ttctggattc ttctaaactc tctaaaaaag      3900
+     aaattctata tctaaataaa gagcgctttg aagaaataac taagaaatct aaagaacaaa      3960
+     tggaacaatt agaacaagaa tctattaatt aatagcaagc ttgaaactaa aaacctaatt      4020
+     tatttaaagc tcaaaataaa aaagagtttt aaaatgggaa attctggttt ttatttgtat      4080
+     aacactgaaa actgcgtctt tgctgataat atcaaagttg ggcaaatgac agagccgctc      4140
+     aaggaccagc aaataatcct tgggacaaca tcaacacctg tcgcagccaa aatgacagct      4200
+     tctgatggaa tatctttaac agtctccaat aattcatcaa ccaatgcttc tattacaatt      4260
+     ggtttggatg cggaaaaagc ttaccagctt attctagaaa agttgggaga tcaaattctt      4320
+     gatggaattg ctgatactat tgttgatagt acagtccaag atattttaga caaaatcaaa      4380
+     acagaccctt ctctaggttt gttgaaagct tttaacaact ttccaatcac taataaaatt      4440
+     caatgcaacg ggttattcac tcccagtaac attgaaactt tattaggagg aactgaaata      4500
+     ggaaaattca cagtcacacc caaaagctct gggagcatgt tcttagtctc agcagatatt      4560
+     attgcatcaa gaatggaagg cggcgttgtt ctagctttgg tacgagaagg tgattctaag      4620
+     ccctgcgcga ttagttatgg atactcatca ggcattccta atttatgtag tctaagaacc      4680
+     agtattacta atacaggatt gactccgaca acgtattcat tacgtgtagg cggtttagaa      4740
+     agcggtgtgg tatgggttaa tgccctttct aatggcaatg atattttagg aataacaaat      4800
+     acttctaatg tatctttttt agaggtaata cctcaaacaa acgcttaaac aatttttatt      4860
+     ggatttttct tataggtttt atatttagag aaaacagttc gaattacggg gtttgttatg      4920
+     caaaataaaa gaaaagtgag ggacgatttt attaaaattg ttaaagatgt gaaaaaagat      4980
+     ttccccgaat tagacctaaa aatacgagta aacaaggaaa aagtaacttt cttaaattct      5040
+     cccttagaac tctaccataa aagtgtctca ctaattctag gactgcttca acaaatagaa      5100
+     aactctttag gattattccc agactctcct gttcttgaaa aattagagga taacagttta      5160
+     aagctaaaaa aggctttgat tatgcttatc ttgtctagaa aagacatgtt ttccaaggct      5220
+     gaatagacaa cttactctaa cgttggagtt gatttgcaca ccttagtttt ttgctctttt      5280
+     aagggaggaa ctggaaaaac aacactttct ctaaacgtgg gatgcaactt ggcccaattt      5340
+     ttagggaaaa aagtgttact tgctgaccta gacccgcaat ccaatttatc ttctggattg      5400
+     ggggctagtg tcagaagtga ccaaaaaggc ttgcacgaca tagtatacac atcaaacgat      5460
+     ttaaaatcaa tcatttgcga aacaaaaaaa gatagtgtgg acctaattcc tgcatcattt      5520
+     tcatccgaac agtttagaga attggatatt catagaggac ctagtaacaa cttaaagtta      5580
+     tttctgaatg agtactgcgc tcctttttat gacatctgca taatagacac tccacctagc      5640
+     ctaggagggt taacgaaaga agcttttgtt gcaggagaca aattaattgc ttgtttaact      5700
+     ccagaacctt tttctattct agggttacaa aagatacgtg aattcttaag ttcggtcgga      5760
+     aaacctgaag aagaacacat tcttggaata gctttgtctt tttgggatga tcgtaactcg      5820
+     actaaccaaa tgtatataga cattatcgag tctatttaca aaaacaagct tttttcaaca      5880
+     aaaattcgtc gagatatttc tctcagccgt tctcttctta aagaagattc tgtagctaat      5940
+     gtctatccaa attctagggc cgcagaagat attctgaagt taacgcatga aatagcaaat      6000
+     attttgcata tcgaatatga acgagattac tctcagagga caacgtgaac aaactaaaaa      6060
+     aagaagcgga tgtctttttt aaaaaaaatc aaactgccgc ttctctagat tttaagaaga      6120
+     cgcttccctc cattgaacta ttctcagcaa ctttgaattc tgaggaaagt cagagtttgg      6180
+     atcgattatt tttatcagag tcccaaaact attcggatga agaattttat caagaagaca      6240
+     tcctagcggt aaaactgctt actggtcaga taaaatccat acagaagcaa cacgtacttc      6300
+     ttttaggaga aaaaatctat aatgctagaa aaatcctgag taaggatcac ttctcctcaa      6360
+     caactttttc atcttggata gagttagttt ttagaactaa gtcttctgct tacaatgctc      6420
+     ttgcatatta cgagcttttt ataaacctcc ccaaccaaac tctacaaaaa gagtttcaat      6480
+     cgatccccta taaatccgca tatattttgg ccgctagaaa aggcgattta aaaaccaagg      6540
+     tcgatgtgat agggaaagta tgtggaatgt cgaactcatc ggcgataagg gtgttggatc      6600
+     aatttcttcc ttcatctaga aacaaagacg ttagagaaac gatagataag tctgattcag      6660
+     agaagaatcg ccaattatct gatttcttaa tagagatact tcgcatcatg tgttccggag      6720
+     tttctttgtc ctcctataac gaaaatcttc tacaacagct ttttgaactt tttaagcaaa      6780
+     agagctgatc ctccgtcagc tcatatatat atatctatta tatatatata tttagggatt      6840
+     tgatttcacg agagagattt gcaactcttg gtggtagact ttgcaactct tggtggtaga      6900
+     ctttgcaact cttggtggta gactttgcaa ctcttggtgg tagacttggt cataatggac      6960
+     ttttgttaaa aaatttatta aaatcttaga gctccgattt tgaatagctt tggttaagaa      7020
+     aatgggctcg atggctttcc ataaaagtag attgttttta acttttgggg acgcgtcgga      7080
+     aatttggtta tctactttat cttatctaac tagaaaaaat tatgcgtctg ggattaactt      7140
+     tcttgtttct ttagagattc tggatttatc ggaaaccttg ataaaggcta tttctcttga      7200
+     ccacagcgaa tctttgttta aaatcaagtc tctagatgtt tttaatggaa aagttgtttc      7260
+     agaggcatct aaacaggcta gagcggcatg ctacatatct ttcacaaagt ttttgtatag      7320
+     attgaccaag ggatatatta aacccgctat tccattgaaa gattttggaa acactacatt      7380
+     ttttaaaatc cgagacaaaa tcaaaacaga atcgatttct aagcaggaat ggacagtttt      7440
+     ttttgaagcg ctccggatag tgaattatag agactattta atcggtaaat tgattgtaca      7500
+     ag                                                                     7502
+//
index 86dcc39..cf10aba 100644 (file)
@@ -25,19 +25,27 @@ import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.AssertJUnit.fail;
 import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
 
-import jalview.gui.JvOptionPane;
-
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.List;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import jalview.bin.Cache;
+import jalview.gui.JvOptionPane;
+
 public class MapListTest
 {
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.initLogger();
+  }
 
   @BeforeClass(alwaysRun = true)
   public void setUpJvOptionPane()
@@ -46,20 +54,23 @@ public class MapListTest
     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
   }
 
-  @Test(groups = { "Functional" })
+  @Test(groups = { "Functional" }, enabled = false)
   public void testSomething()
   {
-    MapList ml = new MapList(new int[] { 1, 5, 10, 15, 25, 20 }, new int[] {
-        51, 1 }, 1, 3);
+    MapList ml = new MapList(new int[] { 1, 5, 10, 15, 25, 20 },
+            new int[]
+            { 51, 1 }, 1, 3);
     MapList ml1 = new MapList(new int[] { 1, 3, 17, 4 },
-            new int[] { 51, 1 }, 1, 3);
+            new int[]
+            { 51, 1 }, 1, 3);
     MapList ml2 = new MapList(new int[] { 1, 60 }, new int[] { 1, 20 }, 3,
             1);
     // test internal consistency
     int to[] = new int[51];
     testMap(ml, 1, 60);
-    MapList mldna = new MapList(new int[] { 2, 2, 6, 8, 12, 16 }, new int[]
-    { 1, 3 }, 3, 1);
+    MapList mldna = new MapList(new int[] { 2, 2, 6, 8, 12, 16 },
+            new int[]
+            { 1, 3 }, 3, 1);
     int[] frm = mldna.locateInFrom(1, 1);
     testLocateFrom(mldna, 1, 1, new int[] { 2, 2, 6, 7 });
     testMap(mldna, 1, 3);
@@ -261,15 +272,25 @@ public class MapListTest
     assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
     assertEquals("[1, 6]", Arrays.toString(ml.locateInFrom(1, 2)));
     assertEquals("[1, 9]", Arrays.toString(ml.locateInFrom(1, 3)));
+    // reversed range treated as if forwards:
+    assertEquals("[1, 9]", Arrays.toString(ml.locateInFrom(3, 1)));
     assertEquals("[1, 12]", Arrays.toString(ml.locateInFrom(1, 4)));
     assertEquals("[4, 9]", Arrays.toString(ml.locateInFrom(2, 3)));
     assertEquals("[4, 12]", Arrays.toString(ml.locateInFrom(2, 4)));
     assertEquals("[7, 12]", Arrays.toString(ml.locateInFrom(3, 4)));
     assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
 
+    /*
+     * partial overlap
+     */
+    assertEquals("[1, 12]", Arrays.toString(ml.locateInFrom(1, 5)));
+    assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(-1, 1)));
+
+    /*
+     * no overlap
+     */
     assertNull(ml.locateInFrom(0, 0));
-    assertNull(ml.locateInFrom(1, 5));
-    assertNull(ml.locateInFrom(-1, 1));
+    
   }
 
   /**
@@ -291,6 +312,28 @@ public class MapListTest
     assertEquals("[10, 10, 12, 12, 14, 14]",
             Arrays.toString(ml.locateInFrom(3, 3)));
     assertEquals("[16, 18]", Arrays.toString(ml.locateInFrom(4, 4)));
+    
+    /*
+     * codons at 11-16, 21-26, 31-36 mapped to peptide positions 1, 3-4, 6-8
+     */
+    ml = new MapList(new int[] { 11, 16, 21, 26, 31, 36 },
+            new int[]
+            { 1, 1, 3, 4, 6, 8 }, 3, 1);
+    assertArrayEquals(new int[] { 11, 13 }, ml.locateInFrom(1, 1));
+    assertArrayEquals(new int[] { 11, 16 }, ml.locateInFrom(1, 3));
+    assertArrayEquals(new int[] { 11, 16, 21, 23 }, ml.locateInFrom(1, 4));
+    assertArrayEquals(new int[] { 14, 16, 21, 23 }, ml.locateInFrom(3, 4));
+
+  }
+
+  @Test(groups = { "Functional" })
+  public void testLocateInFrom_reverseStrand()
+  {
+    int[] codons = new int[] { 12, 1 };
+    int[] protein = new int[] { 1, 4 };
+    MapList ml = new MapList(codons, protein, 3, 1);
+    assertEquals("[12, 10]", Arrays.toString(ml.locateInFrom(1, 1)));
+    assertEquals("[9, 4]", Arrays.toString(ml.locateInFrom(2, 3)));
   }
 
   /**
@@ -315,6 +358,8 @@ public class MapListTest
     assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(1, 12)));
     assertEquals("[2, 2]", Arrays.toString(ml.locateInTo(4, 6)));
     assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(4, 12)));
+    // reverse range treated as if forwards:
+    assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(12, 4)));
 
     /*
      * A part codon is treated as if a whole one.
@@ -326,9 +371,16 @@ public class MapListTest
     assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(3, 11)));
     assertEquals("[2, 4]", Arrays.toString(ml.locateInTo(5, 11)));
 
+    /*
+     * partial overlap
+     */
+    assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(1, 13)));
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(-1, 2)));
+    
+    /*
+     * no overlap
+     */
     assertNull(ml.locateInTo(0, 0));
-    assertNull(ml.locateInTo(1, 13));
-    assertNull(ml.locateInTo(-1, 1));
   }
 
   /**
@@ -350,14 +402,7 @@ public class MapListTest
     MapList ml = new MapList(codons, protein, 3, 1);
 
     /*
-     * Can't map from an unmapped position
-     */
-    assertNull(ml.locateInTo(1, 2));
-    assertNull(ml.locateInTo(2, 4));
-    assertNull(ml.locateInTo(4, 4));
-
-    /*
-     * Valid range or subrange of codon1 maps to protein1.
+     * Valid range or subrange of codon1 maps to protein1
      */
     assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 2)));
     assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(3, 3)));
@@ -371,6 +416,17 @@ public class MapListTest
     // codon positions 7 to 17 (part) cover proteins 2/3/4 at positions 3/4/6
     assertEquals("[3, 4, 6, 6]", Arrays.toString(ml.locateInTo(7, 17)));
 
+    /*
+     * partial overlap
+     */
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 2)));
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 4)));
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(2, 4)));
+    
+    /*
+     * no overlap
+     */
+    assertNull(ml.locateInTo(4, 4));
   }
 
   /**
@@ -431,7 +487,8 @@ public class MapListTest
     List<int[]> ranges = new ArrayList<>();
     ranges.add(new int[] { 2, 3 });
     ranges.add(new int[] { 5, 6 });
-    assertEquals("[2, 3, 5, 6]", Arrays.toString(MapList.getRanges(ranges)));
+    assertEquals("[2, 3, 5, 6]",
+            Arrays.toString(MapList.getRanges(ranges)));
   }
 
   /**
@@ -463,7 +520,8 @@ public class MapListTest
     assertEquals(6, ml2.getToHighest());
     assertEquals("{[2, 3], [5, 7], [9, 10], [12, 12], [14, 14], [16, 18]}",
             prettyPrint(ml2.getFromRanges()));
-    assertEquals("{[1, 1], [3, 4], [6, 6]}", prettyPrint(ml2.getToRanges()));
+    assertEquals("{[1, 1], [3, 4], [6, 6]}",
+            prettyPrint(ml2.getToRanges()));
 
     /*
      * reverse direction
@@ -478,22 +536,23 @@ public class MapListTest
   }
 
   /**
-   * Test constructor can merge consecutive ranges
+   * Test constructor used to merge consecutive ranges but now just leaves them
+   * as supplied (JAL-3751)
    */
   @Test(groups = { "Functional" })
   public void testConstructor_mergeRanges()
   {
-    int[] codons = { 2, 3, 3, 7, 9, 10, 12, 12, 14, 14, 16, 17 };
-    int[] protein = { 1, 1, 1, 3, 6, 6 };
+    int[] codons = { 2, 3, 3, 7, 9, 10, 12, 12, 13, 14, 16, 17 };
+    int[] protein = { 1, 1, 2, 3, 6, 6 };
     MapList ml = new MapList(codons, protein, 3, 1);
     assertEquals(3, ml.getFromRatio());
     assertEquals(2, ml.getFromLowest());
     assertEquals(17, ml.getFromHighest());
     assertEquals(1, ml.getToLowest());
     assertEquals(6, ml.getToHighest());
-    assertEquals("{[2, 7], [9, 10], [12, 12], [14, 14], [16, 17]}",
+    assertEquals("{[2, 3], [3, 7], [9, 10], [12, 12], [13, 14], [16, 17]}",
             prettyPrint(ml.getFromRanges()));
-    assertEquals("{[1, 3], [6, 6]}", prettyPrint(ml.getToRanges()));
+    assertEquals("{[1, 1], [2, 3], [6, 6]}", prettyPrint(ml.getToRanges()));
   }
 
   /**
@@ -544,8 +603,9 @@ public class MapListTest
   @Test(groups = { "Functional" })
   public void testToString()
   {
-    MapList ml = new MapList(new int[] { 1, 5, 10, 15, 25, 20 }, new int[] {
-        51, 1 }, 1, 3);
+    MapList ml = new MapList(new int[] { 1, 5, 10, 15, 25, 20 },
+            new int[]
+            { 51, 1 }, 1, 3);
     String s = ml.toString();
     assertEquals("[ [1, 5] [10, 15] [25, 20] ] 1:3 to [ [51, 1] ]", s);
   }
@@ -554,14 +614,16 @@ public class MapListTest
   public void testAddMapList()
   {
     MapList ml = new MapList(new int[] { 11, 15, 20, 25, 35, 30 },
-            new int[] { 72, 22 }, 1, 3);
+            new int[]
+            { 72, 22 }, 1, 3);
     assertEquals(11, ml.getFromLowest());
     assertEquals(35, ml.getFromHighest());
     assertEquals(22, ml.getToLowest());
     assertEquals(72, ml.getToHighest());
 
-    MapList ml2 = new MapList(new int[] { 2, 4, 37, 40 }, new int[] { 12,
-        17, 78, 83, 88, 96 }, 1, 3);
+    MapList ml2 = new MapList(new int[] { 2, 4, 37, 40 },
+            new int[]
+            { 12, 17, 78, 83, 88, 96 }, 1, 3);
     ml.addMapList(ml2);
     assertEquals(2, ml.getFromLowest());
     assertEquals(40, ml.getFromHighest());
@@ -581,7 +643,8 @@ public class MapListTest
   public void testAddMapList_sameMap()
   {
     MapList ml = new MapList(new int[] { 11, 15, 20, 25, 35, 30 },
-            new int[] { 72, 22 }, 1, 3);
+            new int[]
+            { 72, 22 }, 1, 3);
     String before = ml.toString();
     ml.addMapList(ml);
     assertEquals(before, ml.toString());
@@ -595,8 +658,8 @@ public class MapListTest
     MapList ml = new MapList(new int[] { 11, 15 }, new int[] { 72, 58 }, 1,
             3);
 
-    MapList ml2 = new MapList(new int[] { 15, 16 }, new int[] { 58, 53 },
-            1, 3);
+    MapList ml2 = new MapList(new int[] { 15, 16 }, new int[] { 58, 53 }, 1,
+            3);
     ml.addMapList(ml2);
     assertEquals("[ [11, 16] ] 1:3 to [ [72, 53] ]", ml.toString());
   }
@@ -682,13 +745,15 @@ public class MapListTest
   public void testIsFromForwardStrand()
   {
     // [3-9] declares forward strand
-    MapList ml = new MapList(new int[] { 2, 2, 3, 9, 12, 11 }, new int[] {
-        20, 11 }, 1, 1);
+    MapList ml = new MapList(new int[] { 2, 2, 3, 9, 12, 11 },
+            new int[]
+            { 20, 11 }, 1, 1);
     assertTrue(ml.isFromForwardStrand());
 
     // [11-5] declares reverse strand ([13-14] is ignored)
     ml = new MapList(new int[] { 2, 2, 11, 5, 13, 14 },
-            new int[] { 20, 11 }, 1, 1);
+            new int[]
+            { 20, 11 }, 1, 1);
     assertFalse(ml.isFromForwardStrand());
 
     // all single position ranges - defaults to forward strand
@@ -698,7 +763,7 @@ public class MapListTest
   }
 
   /**
-   * Test the method that merges a list of ranges where possible
+   * Test the method that merges contiguous ranges
    */
   @Test(groups = { "Functional" })
   public void testCoalesceRanges()
@@ -720,101 +785,57 @@ public class MapListTest
     // merging in forward direction:
     ranges.clear();
     ranges.add(new int[] { 1, 3 });
-    ranges.add(new int[] { 4, 5 });
-    ranges.add(new int[] { 5, 5 });
-    ranges.add(new int[] { 5, 7 });
+    ranges.add(new int[] { 4, 5 }); // contiguous
+    ranges.add(new int[] { 5, 5 }); // overlap!
+    ranges.add(new int[] { 6, 7 }); // contiguous
     List<int[]> merged = MapList.coalesceRanges(ranges);
-    assertEquals(1, merged.size());
-    assertArrayEquals(new int[] { 1, 7 }, merged.get(0));
+    assertEquals(2, merged.size());
+    assertArrayEquals(new int[] { 1, 5 }, merged.get(0));
+    assertArrayEquals(new int[] { 5, 7 }, merged.get(1));
     // verify input list is unchanged
     assertEquals(4, ranges.size());
     assertArrayEquals(new int[] { 1, 3 }, ranges.get(0));
     assertArrayEquals(new int[] { 4, 5 }, ranges.get(1));
     assertArrayEquals(new int[] { 5, 5 }, ranges.get(2));
-    assertArrayEquals(new int[] { 5, 7 }, ranges.get(3));
+    assertArrayEquals(new int[] { 6, 7 }, ranges.get(3));
 
     // merging in reverse direction:
     ranges.clear();
     ranges.add(new int[] { 7, 5 });
-    ranges.add(new int[] { 5, 4 });
-    ranges.add(new int[] { 4, 4 });
-    ranges.add(new int[] { 3, 1 });
+    ranges.add(new int[] { 5, 4 }); // overlap
+    ranges.add(new int[] { 4, 4 }); // overlap
+    ranges.add(new int[] { 3, 1 }); // contiguous
     merged = MapList.coalesceRanges(ranges);
-    assertEquals(1, merged.size());
-    assertArrayEquals(new int[] { 7, 1 }, merged.get(0));
+    assertEquals(3, merged.size());
+    assertArrayEquals(new int[] { 7, 5 }, merged.get(0));
+    assertArrayEquals(new int[] { 5, 4 }, merged.get(1));
+    assertArrayEquals(new int[] { 4, 1 }, merged.get(2));
 
     // merging with switches of direction:
     ranges.clear();
     ranges.add(new int[] { 1, 3 });
-    ranges.add(new int[] { 4, 5 });
-    ranges.add(new int[] { 5, 5 });
-    ranges.add(new int[] { 6, 6 });
+    ranges.add(new int[] { 4, 5 }); // contiguous
+    ranges.add(new int[] { 5, 5 }); // overlap
+    ranges.add(new int[] { 6, 6 }); // contiguous
     ranges.add(new int[] { 12, 10 });
-    ranges.add(new int[] { 9, 8 });
-    ranges.add(new int[] { 8, 8 });
-    ranges.add(new int[] { 7, 7 });
+    ranges.add(new int[] { 9, 8 }); // contiguous
+    ranges.add(new int[] { 8, 8 }); // overlap
+    ranges.add(new int[] { 7, 7 }); // contiguous
     merged = MapList.coalesceRanges(ranges);
-    assertEquals(2, merged.size());
-    assertArrayEquals(new int[] { 1, 6 }, merged.get(0));
-    assertArrayEquals(new int[] { 12, 7 }, merged.get(1));
-  }
-
-  /**
-   * Test the method that merges a list of ranges where possible
-   */
-  @Test(groups = { "Functional" })
-  public void testCoalesceRanges_withOverlap()
-  {
-    List<int[]> ranges = new ArrayList<>();
-    ranges.add(new int[] { 1, 3 });
-    ranges.add(new int[] { 2, 5 });
-
-    /*
-     * [2, 5] should extend [1, 3]
-     */
-    List<int[]> merged = MapList.coalesceRanges(ranges);
-    assertEquals(1, merged.size());
+    assertEquals(4, merged.size());
     assertArrayEquals(new int[] { 1, 5 }, merged.get(0));
+    assertArrayEquals(new int[] { 5, 6 }, merged.get(1));
+    assertArrayEquals(new int[] { 12, 8 }, merged.get(2));
+    assertArrayEquals(new int[] { 8, 7 }, merged.get(3));
 
-    /*
-     * a subsumed interval should be dropped
-     */
-    ranges.clear();
-    ranges.add(new int[] { 1, 6 });
-    ranges.add(new int[] { 2, 4 });
-    merged = MapList.coalesceRanges(ranges);
-    assertEquals(1, merged.size());
-    assertArrayEquals(new int[] { 1, 6 }, merged.get(0));
-
-    ranges.clear();
-    ranges.add(new int[] { 1, 5 });
-    ranges.add(new int[] { 1, 6 });
-    merged = MapList.coalesceRanges(ranges);
-    assertEquals(1, merged.size());
-    assertArrayEquals(new int[] { 1, 6 }, merged.get(0));
-
-    /*
-     * merge duplicate ranges
-     */
-    ranges.clear();
-    ranges.add(new int[] { 1, 3 });
-    ranges.add(new int[] { 1, 3 });
-    merged = MapList.coalesceRanges(ranges);
-    assertEquals(1, merged.size());
-    assertArrayEquals(new int[] { 1, 3 }, merged.get(0));
-
-    /*
-     * reverse direction
-     */
+    // 'subsumed' ranges are preserved
     ranges.clear();
-    ranges.add(new int[] { 9, 5 });
-    ranges.add(new int[] { 9, 4 });
-    ranges.add(new int[] { 8, 3 });
-    ranges.add(new int[] { 3, 2 });
-    ranges.add(new int[] { 1, 0 });
+    ranges.add(new int[] { 10, 30 });
+    ranges.add(new int[] { 15, 25 });
     merged = MapList.coalesceRanges(ranges);
-    assertEquals(1, merged.size());
-    assertArrayEquals(new int[] { 9, 0 }, merged.get(0));
+    assertEquals(2, merged.size());
+    assertArrayEquals(new int[] { 10, 30 }, merged.get(0));
+    assertArrayEquals(new int[] { 15, 25 }, merged.get(1));
   }
 
   /**
@@ -826,13 +847,15 @@ public class MapListTest
     /*
      * simple 1:1 plus 1:1 forwards
      */
-    MapList ml1 = new MapList(new int[] { 3, 4, 8, 12 }, new int[] { 5, 8,
-        11, 13 }, 1, 1);
+    MapList ml1 = new MapList(new int[] { 3, 4, 8, 12 },
+            new int[]
+            { 5, 8, 11, 13 }, 1, 1);
     assertEquals("{[3, 4], [8, 12]}", prettyPrint(ml1.getFromRanges()));
     assertEquals("{[5, 8], [11, 13]}", prettyPrint(ml1.getToRanges()));
 
-    MapList ml2 = new MapList(new int[] { 1, 50 }, new int[] { 40, 45, 70,
-        75, 90, 127 }, 1, 1);
+    MapList ml2 = new MapList(new int[] { 1, 50 },
+            new int[]
+            { 40, 45, 70, 75, 90, 127 }, 1, 1);
     assertEquals("{[1, 50]}", prettyPrint(ml2.getFromRanges()));
     assertEquals("{[40, 45], [70, 75], [90, 127]}",
             prettyPrint(ml2.getToRanges()));
@@ -859,7 +882,8 @@ public class MapListTest
      */
     ml1 = new MapList(new int[] { 1, 50 }, new int[] { 70, 119 }, 1, 1);
     ml2 = new MapList(new int[] { 1, 500 },
-            new int[] { 1000, 901, 600, 201 }, 1, 1);
+            new int[]
+            { 1000, 901, 600, 201 }, 1, 1);
     compound = ml1.traverse(ml2);
 
     assertEquals(1, compound.getFromRatio());
@@ -870,14 +894,14 @@ public class MapListTest
     toRanges = compound.getToRanges();
     assertEquals(2, toRanges.size());
     assertArrayEquals(new int[] { 931, 901 }, toRanges.get(0));
-    assertArrayEquals(new int[] { 600, 582 }, toRanges.get(1));
+    assertArrayEquals(new int[] { 600, 582}, toRanges.get(1));
 
     /*
      * 1:1 plus 1:3 should result in 1:3
      */
     ml1 = new MapList(new int[] { 1, 30 }, new int[] { 11, 40 }, 1, 1);
-    ml2 = new MapList(new int[] { 1, 100 }, new int[] { 1, 50, 91, 340 },
-            1, 3);
+    ml2 = new MapList(new int[] { 1, 100 }, new int[] { 1, 50, 91, 340 }, 1,
+            3);
     compound = ml1.traverse(ml2);
 
     assertEquals(1, compound.getFromRatio());
@@ -895,8 +919,8 @@ public class MapListTest
      * 3:1 plus 1:1 should result in 3:1
      */
     ml1 = new MapList(new int[] { 1, 30 }, new int[] { 11, 20 }, 3, 1);
-    ml2 = new MapList(new int[] { 1, 100 }, new int[] { 1, 15, 91, 175 },
-            1, 1);
+    ml2 = new MapList(new int[] { 1, 100 }, new int[] { 1, 15, 91, 175 }, 1,
+            1);
     compound = ml1.traverse(ml2);
 
     assertEquals(3, compound.getFromRatio());
@@ -949,17 +973,17 @@ public class MapListTest
     assertArrayEquals(new int[] { 71, 126 }, toRanges.get(1));
 
     /*
-     * method returns null if not all regions are mapped through
+     * if not all regions are mapped through, returns what is
      */
     ml1 = new MapList(new int[] { 1, 50 }, new int[] { 101, 150 }, 1, 1);
-    ml2 = new MapList(new int[] { 131, 180 }, new int[] { 201, 250 }, 1, 3);
+    ml2 = new MapList(new int[] { 131, 180 }, new int[] { 201, 250 }, 1, 1);
     compound = ml1.traverse(ml2);
     assertNull(compound);
   }
 
   /**
-   * Test that method that inspects for the (first) forward or reverse 'to' range.
-   * Single position ranges are ignored.
+   * Test that method that inspects for the (first) forward or reverse 'to'
+   * range. Single position ranges are ignored.
    */
   @Test(groups = { "Functional" })
   public void testIsToForwardsStrand()
@@ -981,4 +1005,427 @@ public class MapListTest
             1);
     assertTrue(ml.isToForwardStrand());
   }
+
+  /**
+   * Test for mapping with overlapping ranges
+   */
+  @Test(groups = { "Functional" })
+  public void testLocateInFrom_withOverlap()
+  {
+    /*
+     * gene to protein...
+     */
+    int[] codons = new int[] { 1, 12, 12, 17 };
+    int[] protein = new int[] { 1, 6 };
+    MapList ml = new MapList(codons, protein, 3, 1);
+    assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(1, 1)));
+    assertEquals("[4, 6]", Arrays.toString(ml.locateInFrom(2, 2)));
+    assertEquals("[7, 9]", Arrays.toString(ml.locateInFrom(3, 3)));
+    assertEquals("[10, 12]", Arrays.toString(ml.locateInFrom(4, 4)));
+    assertEquals("[12, 14]", Arrays.toString(ml.locateInFrom(5, 5)));
+    assertEquals("[15, 17]", Arrays.toString(ml.locateInFrom(6, 6)));
+    assertEquals("[1, 6]", Arrays.toString(ml.locateInFrom(1, 2)));
+    assertEquals("[1, 9]", Arrays.toString(ml.locateInFrom(1, 3)));
+    assertEquals("[1, 12]", Arrays.toString(ml.locateInFrom(1, 4)));
+    assertEquals("[1, 12, 12, 14]", Arrays.toString(ml.locateInFrom(1, 5)));
+    assertEquals("[1, 12, 12, 17]", Arrays.toString(ml.locateInFrom(1, 6)));
+    assertEquals("[4, 9]", Arrays.toString(ml.locateInFrom(2, 3)));
+    assertEquals("[7, 12, 12, 17]", Arrays.toString(ml.locateInFrom(3, 6)));
+
+    /*
+     * partial overlap of range
+     */
+    assertEquals("[4, 12, 12, 17]", Arrays.toString(ml.locateInFrom(2, 7)));
+    assertEquals("[1, 3]", Arrays.toString(ml.locateInFrom(-1, 1)));
+
+    /*
+     * no overlap in range
+     */
+    assertNull(ml.locateInFrom(0, 0));
+
+    /*
+     * gene to CDS...from EMBL:MN908947
+     */
+    int[] gene = new int[] { 266, 13468, 13468, 21555 };
+    int[] cds = new int[] { 1, 21291 };
+    ml = new MapList(gene, cds, 1, 1);
+    assertEquals("[13468, 13468]",
+            Arrays.toString(ml.locateInFrom(13203, 13203)));
+    assertEquals("[13468, 13468]",
+            Arrays.toString(ml.locateInFrom(13204, 13204)));
+    assertEquals("[13468, 13468, 13468, 13468]",
+            Arrays.toString(ml.locateInFrom(13203, 13204)));
+  }
+
+  /**
+   * Test for mapping with overlapping ranges
+   */
+  @Test(groups = { "Functional" })
+  public void testLocateInTo_withOverlap()
+  {
+    /*
+     * gene to protein...
+     */
+    int[] codons = new int[] { 1, 12, 12, 17 };
+    int[] protein = new int[] { 1, 6 };
+    MapList ml = new MapList(codons, protein, 3, 1);
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(1, 1)));
+    assertEquals("[1, 3]", Arrays.toString(ml.locateInTo(3, 8)));
+    assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(2, 11)));
+    assertEquals("[1, 4]", Arrays.toString(ml.locateInTo(3, 11)));
+
+    // we want base 12 to map to both of the amino acids it codes for
+    assertEquals("[4, 5]", Arrays.toString(ml.locateInTo(12, 12)));
+    assertEquals("[4, 5]", Arrays.toString(ml.locateInTo(11, 12)));
+    assertEquals("[4, 6]", Arrays.toString(ml.locateInTo(11, 15)));
+    assertEquals("[6, 6]", Arrays.toString(ml.locateInTo(15, 17)));
+
+    /*
+     * no overlap
+     */
+    assertNull(ml.locateInTo(0, 0));
+    
+    /*
+     * partial overlap
+     */
+    assertEquals("[1, 6]", Arrays.toString(ml.locateInTo(1, 18)));
+    assertEquals("[1, 1]", Arrays.toString(ml.locateInTo(-1, 1)));
+
+    /*
+     * gene to CDS...from EMBL:MN908947
+     * the base at 13468 is used twice in transcription
+     */
+    int[] gene = new int[] { 266, 13468, 13468, 21555 };
+    int[] cds = new int[] { 1, 21291 };
+    ml = new MapList(gene, cds, 1, 1);
+    assertEquals("[13203, 13204]",
+            Arrays.toString(ml.locateInTo(13468, 13468)));
+    
+    /*
+     * gene to protein
+     * the base at 13468 is in the codon for 4401N and also 4402R
+     */
+    gene = new int[] { 266, 13468, 13468, 21552 }; // stop codon excluded
+    protein = new int[] { 1, 7096 };
+    ml = new MapList(gene, protein, 3, 1);
+    assertEquals("[4401, 4402]",
+            Arrays.toString(ml.locateInTo(13468, 13468)));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testTraverseToPosition()
+  {
+    List<int[]> ranges = new ArrayList<>();
+    assertNull(MapList.traverseToPosition(ranges, 0));
+
+    ranges.add(new int[] { 3, 6 });
+    assertNull(MapList.traverseToPosition(ranges, 0));
+  }
+
+  @Test(groups = { "Functional" })
+  public void testCountPositions()
+  {
+    try
+    {
+      MapList.countPositions(null, 1);
+      fail("expected exception");
+    } catch (NullPointerException e)
+    {
+      // expected
+    }
+
+    List<int[]> intervals = new ArrayList<>();
+    assertNull(MapList.countPositions(intervals, 1));
+
+    /*
+     * forward strand
+     */
+    intervals.add(new int[] { 10, 20 });
+    assertNull(MapList.countPositions(intervals, 9));
+    assertNull(MapList.countPositions(intervals, 21));
+    assertArrayEquals(new int[] { 1, 1 },
+            MapList.countPositions(intervals, 10));
+    assertArrayEquals(new int[] { 6, 1 },
+            MapList.countPositions(intervals, 15));
+    assertArrayEquals(new int[] { 11, 1 },
+            MapList.countPositions(intervals, 20));
+
+    intervals.add(new int[] { 25, 25 });
+    assertArrayEquals(new int[] { 12, 1 },
+            MapList.countPositions(intervals, 25));
+
+    // next interval repeats position 25 - which should be counted twice if
+    // traversed
+    intervals.add(new int[] { 25, 26 });
+    assertArrayEquals(new int[] { 12, 1 },
+            MapList.countPositions(intervals, 25));
+    assertArrayEquals(new int[] { 14, 1 },
+            MapList.countPositions(intervals, 26));
+
+    /*
+     * reverse strand
+     */
+    intervals.clear();
+    intervals.add(new int[] { 5, -5 });
+    assertNull(MapList.countPositions(intervals, 6));
+    assertNull(MapList.countPositions(intervals, -6));
+    assertArrayEquals(new int[] { 1, -1 },
+            MapList.countPositions(intervals, 5));
+    assertArrayEquals(new int[] { 7, -1 },
+            MapList.countPositions(intervals, -1));
+    assertArrayEquals(new int[] { 11, -1 },
+            MapList.countPositions(intervals, -5));
+
+    /*
+     * reverse then forward
+     */
+    intervals.add(new int[] { 5, 10 });
+    assertArrayEquals(new int[] { 13, 1 },
+            MapList.countPositions(intervals, 6));
+
+    /*
+     * reverse then forward then reverse
+     */
+    intervals.add(new int[] { -10, -20 });
+    assertArrayEquals(new int[] { 20, -1 },
+            MapList.countPositions(intervals, -12));
+
+    /*
+     * an interval [x, x] is treated as forward
+     */
+    intervals.add(new int[] { 30, 30 });
+    assertArrayEquals(new int[] { 29, 1 },
+            MapList.countPositions(intervals, 30));
+
+    /*
+     * it is the first matched occurrence that is returned
+     */
+    intervals.clear();
+    intervals.add(new int[] { 1, 2 });
+    intervals.add(new int[] { 2, 3 });
+    assertArrayEquals(new int[] { 2, 1 },
+            MapList.countPositions(intervals, 2));
+    intervals.add(new int[] { -1, -2 });
+    intervals.add(new int[] { -2, -3 });
+    assertArrayEquals(new int[] { 6, -1 },
+            MapList.countPositions(intervals, -2));
+  }
+
+  /**
+   * Tests for helper method that adds any overlap (plus offset) to a set of
+   * overlaps
+   */
+  @Test(groups = { "Functional" })
+  public void testAddOffsetPositions()
+  {
+    List<int[]> mapped = new ArrayList<>();
+    int[] range = new int[] {10, 20};
+    BitSet offsets = new BitSet();
+
+    MapList.addOffsetPositions(mapped, 0, range, offsets);
+    assertTrue(mapped.isEmpty()); // nothing marked for overlap
+
+    offsets.set(11);
+    MapList.addOffsetPositions(mapped, 0, range, offsets);
+    assertTrue(mapped.isEmpty()); // no offset 11 in range
+
+    offsets.set(4, 6); // this sets bits 4 and 5
+    MapList.addOffsetPositions(mapped, 0, range, offsets);
+    assertEquals(1, mapped.size());
+    assertArrayEquals(new int[] { 14, 15 }, mapped.get(0));
+
+    mapped.clear();
+    offsets.set(10);
+    MapList.addOffsetPositions(mapped, 0, range, offsets);
+    assertEquals(2, mapped.size());
+    assertArrayEquals(new int[] { 14, 15 }, mapped.get(0));
+    assertArrayEquals(new int[] { 20, 20 }, mapped.get(1));
+
+    /*
+     * reverse range
+     */
+    range = new int[] { 20, 10 };
+    mapped.clear();
+    offsets.clear();
+    MapList.addOffsetPositions(mapped, 0, range, offsets);
+    assertTrue(mapped.isEmpty()); // nothing marked for overlap
+    offsets.set(11);
+    MapList.addOffsetPositions(mapped, 0, range, offsets);
+    assertTrue(mapped.isEmpty()); // no offset 11 in range
+    offsets.set(0);
+    offsets.set(10);
+    offsets.set(6, 8); // sets bits 6 and 7
+    MapList.addOffsetPositions(mapped, 0, range, offsets);
+    assertEquals(3, mapped.size());
+    assertArrayEquals(new int[] { 20, 20 }, mapped.get(0));
+    assertArrayEquals(new int[] { 14, 13 }, mapped.get(1));
+    assertArrayEquals(new int[] { 10, 10 }, mapped.get(2));
+  }
+  
+  @Test(groups = { "Functional" })
+  public void testGetPositionsForOffsets()
+  {
+    List<int[]> ranges = new ArrayList<>();
+    BitSet offsets = new BitSet();
+    List<int[]> mapped = MapList.getPositionsForOffsets(ranges, offsets);
+    assertTrue(mapped.isEmpty()); // no ranges and no offsets!
+    
+    offsets.set(5, 1000);
+    mapped = MapList.getPositionsForOffsets(ranges, offsets);
+    assertTrue(mapped.isEmpty()); // no ranges
+    
+    /*
+     * one range with overlap of offsets
+     */
+    ranges.add(new int[] {15, 25});
+    mapped = MapList.getPositionsForOffsets(ranges, offsets);
+    assertEquals(1, mapped.size());
+    assertArrayEquals(new int[] {20,  25}, mapped.get(0));
+    
+    /*
+     * two ranges
+     */
+    ranges.add(new int[] {300, 320});
+    mapped = MapList.getPositionsForOffsets(ranges, offsets);
+    assertEquals(2, mapped.size());
+    assertArrayEquals(new int[] {20,  25}, mapped.get(0));
+    assertArrayEquals(new int[] {300, 320}, mapped.get(1));
+    
+    /*
+     * boundary case - right end of first range overlaps
+     */
+    offsets.clear();
+    offsets.set(10);
+    mapped = MapList.getPositionsForOffsets(ranges, offsets);
+    assertEquals(1, mapped.size());
+    assertArrayEquals(new int[] {25,  25}, mapped.get(0));
+    
+    /*
+     * boundary case - left end of second range overlaps
+     */
+    offsets.set(11);
+    mapped = MapList.getPositionsForOffsets(ranges, offsets);
+    assertEquals(2, mapped.size());
+    assertArrayEquals(new int[] {25,  25}, mapped.get(0));
+    assertArrayEquals(new int[] {300, 300}, mapped.get(1));
+    
+    /*
+     * offsets into a circular range are reported in
+     * the order in which they are traversed
+     */
+    ranges.clear();
+    ranges.add(new int[] {100, 150});
+    ranges.add(new int[] {60, 80});
+    offsets.clear();
+    offsets.set(45, 55); // sets bits 45 to 54
+    mapped = MapList.getPositionsForOffsets(ranges, offsets);
+    assertEquals(2, mapped.size());
+    assertArrayEquals(new int[] {145, 150}, mapped.get(0)); // offsets 45-50
+    assertArrayEquals(new int[] {60, 63}, mapped.get(1)); // offsets 51-54
+
+    /*
+     * reverse range overlap is reported with start < end
+     */
+    ranges.clear();
+    ranges.add(new int[] {4321, 4000});
+    offsets.clear();
+    offsets.set(20, 22); // sets bits 20 and 21
+    offsets.set(30);
+    mapped = MapList.getPositionsForOffsets(ranges, offsets);
+    assertEquals(2, mapped.size());
+    assertArrayEquals(new int[] {4301, 4300}, mapped.get(0));
+    assertArrayEquals(new int[] {4291, 4291}, mapped.get(1));
+  }
+  
+  @Test(groups = { "Functional" })
+  public void testGetMappedOffsetsForPositions()
+  {
+    /*
+     * start by verifying the examples in the method's Javadoc!
+     */
+    List<int[]> ranges = new ArrayList<>();
+    ranges.add(new int[] {10, 20});
+    ranges.add(new int[] {31, 40});
+    BitSet overlaps = MapList.getMappedOffsetsForPositions(1, 9, ranges, 1, 1);
+    assertTrue(overlaps.isEmpty());
+    overlaps = MapList.getMappedOffsetsForPositions(1, 11, ranges, 1, 1);
+    assertEquals(2, overlaps.cardinality());
+    assertTrue(overlaps.get(0));
+    assertTrue(overlaps.get(1));
+    overlaps = MapList.getMappedOffsetsForPositions(15, 35, ranges, 1, 1);
+    assertEquals(11, overlaps.cardinality());
+    for (int i = 5 ; i <= 11 ; i++)
+    {
+      assertTrue(overlaps.get(i));
+    }
+    
+    ranges.clear();
+    ranges.add(new int[] {1, 200});
+    overlaps = MapList.getMappedOffsetsForPositions(9, 9, ranges, 1, 3);
+    assertEquals(3, overlaps.cardinality());
+    assertTrue(overlaps.get(24));
+    assertTrue(overlaps.get(25));
+    assertTrue(overlaps.get(26));
+    
+    ranges.clear();
+    ranges.add(new int[] {101, 150});
+    ranges.add(new int[] {171, 180});
+    overlaps = MapList.getMappedOffsetsForPositions(101, 102, ranges, 3, 1);
+    assertEquals(1, overlaps.cardinality());
+    assertTrue(overlaps.get(0));
+    overlaps = MapList.getMappedOffsetsForPositions(150, 171, ranges, 3, 1);
+    assertEquals(1, overlaps.cardinality());
+    assertTrue(overlaps.get(16));
+    
+    ranges.clear();
+    ranges.add(new int[] {101, 150});
+    ranges.add(new int[] {21, 30});
+    overlaps = MapList.getMappedOffsetsForPositions(24, 40, ranges, 3, 1);
+    assertEquals(3, overlaps.cardinality());
+    assertTrue(overlaps.get(17));
+    assertTrue(overlaps.get(18));
+    assertTrue(overlaps.get(19));
+    
+    /*
+     * reverse range 1:1 (e.g. reverse strand gene to transcript)
+     */
+    ranges.clear();
+    ranges.add(new int[] {20, 10});
+    overlaps = MapList.getMappedOffsetsForPositions(12, 13, ranges, 1, 1);
+    assertEquals(2, overlaps.cardinality());
+    assertTrue(overlaps.get(7));
+    assertTrue(overlaps.get(8));
+    
+    /*
+     * reverse range 3:1 (e.g. reverse strand gene to peptide)
+     * from EMBL:J03321 to P0CE20
+     */
+    ranges.clear();
+    ranges.add(new int[] {1480, 488});
+    overlaps = MapList.getMappedOffsetsForPositions(1460, 1460, ranges, 3, 1);
+    // 1460 is the end of the 7th codon
+    assertEquals(1, overlaps.cardinality());
+    assertTrue(overlaps.get(6));
+    // add one base (part codon)
+    overlaps = MapList.getMappedOffsetsForPositions(1459, 1460, ranges, 3, 1);
+    assertEquals(2, overlaps.cardinality());
+    assertTrue(overlaps.get(6));
+    assertTrue(overlaps.get(7));
+    // add second base (part codon)
+    overlaps = MapList.getMappedOffsetsForPositions(1458, 1460, ranges, 3, 1);
+    assertEquals(2, overlaps.cardinality());
+    assertTrue(overlaps.get(6));
+    assertTrue(overlaps.get(7));
+    // add third base (whole codon)
+    overlaps = MapList.getMappedOffsetsForPositions(1457, 1460, ranges, 3, 1);
+    assertEquals(2, overlaps.cardinality());
+    assertTrue(overlaps.get(6));
+    assertTrue(overlaps.get(7));
+    // add one more base (part codon)
+    overlaps = MapList.getMappedOffsetsForPositions(1456, 1460, ranges, 3, 1);
+    assertEquals(3, overlaps.cardinality());
+    assertTrue(overlaps.get(6));
+    assertTrue(overlaps.get(7));
+    assertTrue(overlaps.get(8));
+  }
 }
index 097ccd4..3418f3c 100644 (file)
@@ -24,8 +24,20 @@ import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
+import static org.testng.AssertJUnit.fail;
+
+import java.awt.Color;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
 
 import jalview.api.AlignViewportI;
+import jalview.bin.Cache;
 import jalview.commands.EditCommand;
 import jalview.commands.EditCommand.Action;
 import jalview.commands.EditCommand.Edit;
@@ -46,18 +58,13 @@ import jalview.io.FileFormat;
 import jalview.io.FileFormatI;
 import jalview.io.FormatAdapter;
 
-import java.awt.Color;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
 public class MappingUtilsTest
 {
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    Cache.initLogger();
+  }
 
   @BeforeClass(alwaysRun = true)
   public void setUpJvOptionPane()
@@ -89,8 +96,9 @@ public class MappingUtilsTest
     MapList map = new MapList(new int[] { 5, 10 }, new int[] { 12, 13 }, 3,
             1);
     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
-    List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
-    { acf });
+    List<AlignedCodonFrame> acfList = Arrays
+            .asList(new AlignedCodonFrame[]
+            { acf });
 
     /*
      * Check protein residue 12 maps to codon 5-7, 13 to codon 8-10
@@ -139,11 +147,14 @@ public class MappingUtilsTest
      * Map dna bases [6, 8, 9], [11, 13, 115] to protein residues 8 and 9
      */
     AlignedCodonFrame acf = new AlignedCodonFrame();
-    MapList map = new MapList(new int[] { 6, 6, 8, 9, 11, 11, 13, 13, 15,
-        15 }, new int[] { 8, 9 }, 3, 1);
+    MapList map = new MapList(
+            new int[]
+            { 6, 6, 8, 9, 11, 11, 13, 13, 15, 15 }, new int[] { 8, 9 }, 3,
+            1);
     acf.addMap(seq1.getDatasetSequence(), aseq1.getDatasetSequence(), map);
-    List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
-    { acf });
+    List<AlignedCodonFrame> acfList = Arrays
+            .asList(new AlignedCodonFrame[]
+            { acf });
 
     /*
      * Check protein residue 8 maps to [6, 8, 9]
@@ -183,8 +194,8 @@ public class MappingUtilsTest
     for (int i = 5; i < 18; i++)
     {
       sr = MappingUtils.buildSearchResults(seq1, i, acfList);
-      int residue = (i == 6 || i == 8 || i == 9) ? 8 : (i == 11 || i == 13
-              || i == 15 ? 9 : 0);
+      int residue = (i == 6 || i == 8 || i == 9) ? 8
+              : (i == 11 || i == 13 || i == 15 ? 9 : 0);
       if (residue == 0)
       {
         assertEquals(0, sr.getResults().size());
@@ -220,11 +231,12 @@ public class MappingUtilsTest
     MapList map = new MapList(new int[] { 1, 3 }, new int[] { 1, 1 }, 3, 1);
     for (int seq = 0; seq < 3; seq++)
     {
-      acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
-              .getSequenceAt(seq).getDatasetSequence(), map);
+      acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(),
+              protein.getSequenceAt(seq).getDatasetSequence(), map);
     }
-    List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
-    { acf });
+    List<AlignedCodonFrame> acfList = Arrays
+            .asList(new AlignedCodonFrame[]
+            { acf });
 
     AlignViewportI dnaView = new AlignViewport(cdna);
     AlignViewportI proteinView = new AlignViewport(protein);
@@ -285,8 +297,8 @@ public class MappingUtilsTest
   protected AlignmentI loadAlignment(final String data, FileFormatI format)
           throws IOException
   {
-    AlignmentI a = new FormatAdapter().readFile(data,
-            DataSourceType.PASTE, format);
+    AlignmentI a = new FormatAdapter().readFile(data, DataSourceType.PASTE,
+            format);
     a.setDataset(null);
     return a;
   }
@@ -331,8 +343,8 @@ public class MappingUtilsTest
     cs.clear();
     colsel.clear();
     colsel.addElement(2);
-    MappingUtils.mapColumnSelection(colsel, hidden, proteinView,
-            dnaView, cs, hs);
+    MappingUtils.mapColumnSelection(colsel, hidden, proteinView, dnaView,
+            cs, hs);
     assertEquals("[]", cs.getSelected().toString());
 
     /*
@@ -342,8 +354,8 @@ public class MappingUtilsTest
     cs.clear();
     colsel.clear();
     colsel.addElement(3);
-    MappingUtils.mapColumnSelection(colsel, hidden, proteinView,
-            dnaView, cs, hs);
+    MappingUtils.mapColumnSelection(colsel, hidden, proteinView, dnaView,
+            cs, hs);
     assertEquals("[5, 6, 7, 8, 9, 10]", cs.getSelected().toString());
 
     /*
@@ -354,10 +366,10 @@ public class MappingUtilsTest
     colsel.clear();
     colsel.addElement(1);
     colsel.addElement(3);
-    MappingUtils.mapColumnSelection(colsel, hidden, proteinView,
-            dnaView, cs, hs);
-    assertEquals("[0, 1, 2, 3, 5, 6, 7, 8, 9, 10]", cs.getSelected()
-            .toString());
+    MappingUtils.mapColumnSelection(colsel, hidden, proteinView, dnaView,
+            cs, hs);
+    assertEquals("[0, 1, 2, 3, 5, 6, 7, 8, 9, 10]",
+            cs.getSelected().toString());
   }
 
   /**
@@ -386,23 +398,27 @@ public class MappingUtilsTest
     // map first dna to first protein seq
     AlignedCodonFrame acf = new AlignedCodonFrame();
     MapList map = new MapList(new int[] { 10, 12, 15, 15, 17, 18 },
-            new int[] { 40, 41 }, 3, 1);
-    acf.addMap(cdna.getSequenceAt(0).getDatasetSequence(), protein
-            .getSequenceAt(0).getDatasetSequence(), map);
+            new int[]
+            { 40, 41 }, 3, 1);
+    acf.addMap(cdna.getSequenceAt(0).getDatasetSequence(),
+            protein.getSequenceAt(0).getDatasetSequence(), map);
 
     // map second dna to second protein seq
-    map = new MapList(new int[] { 20, 20, 22, 23, 24, 26 }, new int[] { 50,
-        51 }, 3, 1);
-    acf.addMap(cdna.getSequenceAt(1).getDatasetSequence(), protein
-            .getSequenceAt(1).getDatasetSequence(), map);
+    map = new MapList(new int[] { 20, 20, 22, 23, 24, 26 },
+            new int[]
+            { 50, 51 }, 3, 1);
+    acf.addMap(cdna.getSequenceAt(1).getDatasetSequence(),
+            protein.getSequenceAt(1).getDatasetSequence(), map);
 
     // map third dna to third protein seq
-    map = new MapList(new int[] { 30, 30, 32, 34, 36, 37 }, new int[] { 60,
-        61 }, 3, 1);
-    acf.addMap(cdna.getSequenceAt(2).getDatasetSequence(), protein
-            .getSequenceAt(2).getDatasetSequence(), map);
-    List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
-    { acf });
+    map = new MapList(new int[] { 30, 30, 32, 34, 36, 37 },
+            new int[]
+            { 60, 61 }, 3, 1);
+    acf.addMap(cdna.getSequenceAt(2).getDatasetSequence(),
+            protein.getSequenceAt(2).getDatasetSequence(), map);
+    List<AlignedCodonFrame> acfList = Arrays
+            .asList(new AlignedCodonFrame[]
+            { acf });
 
     dnaView = new AlignViewport(cdna);
     proteinView = new AlignViewport(protein);
@@ -465,24 +481,21 @@ public class MappingUtilsTest
   public void testFlattenRanges()
   {
     assertEquals("[1, 2, 3, 4]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 4 })));
-    assertEquals(
-            "[1, 2, 3, 4]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 2, 3,
-                4 })));
-    assertEquals(
-            "[1, 2, 3, 4]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 1, 2,
-                2, 3, 3, 4, 4 })));
-    assertEquals(
-            "[1, 2, 3, 4, 7, 8, 9, 12]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 4, 7,
-                9, 12, 12 })));
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 1, 4 })));
+    assertEquals("[1, 2, 3, 4]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 1, 2, 3, 4 })));
+    assertEquals("[1, 2, 3, 4]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 1, 1, 2, 2, 3, 3, 4, 4 })));
+    assertEquals("[1, 2, 3, 4, 7, 8, 9, 12]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 1, 4, 7, 9, 12, 12 })));
     // trailing unpaired start position is ignored:
-    assertEquals(
-            "[1, 2, 3, 4, 7, 8, 9, 12]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 1, 4, 7,
-                9, 12, 12, 15 })));
+    assertEquals("[1, 2, 3, 4, 7, 8, 9, 12]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 1, 4, 7, 9, 12, 12, 15 })));
   }
 
   /**
@@ -508,11 +521,12 @@ public class MappingUtilsTest
     MapList map = new MapList(new int[] { 1, 6 }, new int[] { 1, 2 }, 3, 1);
     for (int seq = 0; seq < 3; seq++)
     {
-      acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
-              .getSequenceAt(seq).getDatasetSequence(), map);
+      acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(),
+              protein.getSequenceAt(seq).getDatasetSequence(), map);
     }
-    List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
-    { acf });
+    List<AlignedCodonFrame> acfList = Arrays
+            .asList(new AlignedCodonFrame[]
+            { acf });
 
     AlignViewportI dnaView = new AlignViewport(cdna);
     AlignViewportI proteinView = new AlignViewport(protein);
@@ -585,17 +599,19 @@ public class MappingUtilsTest
             FileFormat.Fasta);
     cdna.setDataset(null);
     AlignmentI protein = loadAlignment(
-            ">Seq1\n-KA-S\n>Seq2\n--L-QY\n>Seq3\nQ-V-M\n", FileFormat.Fasta);
+            ">Seq1\n-KA-S\n>Seq2\n--L-QY\n>Seq3\nQ-V-M\n",
+            FileFormat.Fasta);
     protein.setDataset(null);
     AlignedCodonFrame acf = new AlignedCodonFrame();
     MapList map = new MapList(new int[] { 1, 9 }, new int[] { 1, 3 }, 3, 1);
     for (int seq = 0; seq < 3; seq++)
     {
-      acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(), protein
-              .getSequenceAt(seq).getDatasetSequence(), map);
+      acf.addMap(cdna.getSequenceAt(seq).getDatasetSequence(),
+              protein.getSequenceAt(seq).getDatasetSequence(), map);
     }
-    List<AlignedCodonFrame> acfList = Arrays.asList(new AlignedCodonFrame[]
-    { acf });
+    List<AlignedCodonFrame> acfList = Arrays
+            .asList(new AlignedCodonFrame[]
+            { acf });
 
     AlignViewportI dnaView = new AlignViewport(cdna);
     AlignViewportI proteinView = new AlignViewport(protein);
@@ -698,8 +714,8 @@ public class MappingUtilsTest
     /*
      * Seq1 has three mappings
      */
-    List<AlignedCodonFrame> result = MappingUtils.findMappingsForSequence(
-            seq1, mappings);
+    List<AlignedCodonFrame> result = MappingUtils
+            .findMappingsForSequence(seq1, mappings);
     assertEquals(3, result.size());
     assertTrue(result.contains(acf1));
     assertTrue(result.contains(acf2));
@@ -776,22 +792,22 @@ public class MappingUtilsTest
      */
     List<AlignedCodonFrame> result = MappingUtils
             .findMappingsForSequenceAndOthers(null, mappings,
-                    Arrays.asList(new SequenceI[] { seq1, seq2 }));
+                    Arrays.asList(new SequenceI[]
+                    { seq1, seq2 }));
     assertTrue(result.isEmpty());
 
     result = MappingUtils.findMappingsForSequenceAndOthers(seq1, null,
-            Arrays.asList(new SequenceI[] { seq1, seq2 }));
+            Arrays.asList(new SequenceI[]
+            { seq1, seq2 }));
     assertTrue(result.isEmpty());
 
     /*
      * Seq1 has three mappings, but filter argument will only accept
      * those to seq2
      */
-    result = MappingUtils.findMappingsForSequenceAndOthers(
-            seq1,
-            mappings,
-            Arrays.asList(new SequenceI[] { seq1, seq2,
-                seq1.getDatasetSequence() }));
+    result = MappingUtils.findMappingsForSequenceAndOthers(seq1, mappings,
+            Arrays.asList(new SequenceI[]
+            { seq1, seq2, seq1.getDatasetSequence() }));
     assertEquals(2, result.size());
     assertTrue(result.contains(acf1));
     assertTrue(result.contains(acf2));
@@ -820,7 +836,8 @@ public class MappingUtilsTest
     dna.createDatasetSequence();
     protein.createDatasetSequence();
     AlignedCodonFrame acf = new AlignedCodonFrame();
-    MapList map = new MapList(new int[] { 8, 16 }, new int[] { 5, 7 }, 3, 1);
+    MapList map = new MapList(new int[] { 8, 16 }, new int[] { 5, 7 }, 3,
+            1);
     acf.addMap(dna.getDatasetSequence(), protein.getDatasetSequence(), map);
     List<AlignedCodonFrame> mappings = new ArrayList<>();
     mappings.add(acf);
@@ -835,7 +852,8 @@ public class MappingUtilsTest
      */
     EditCommand ec = new EditCommand();
     final Edit edit = ec.new Edit(Action.INSERT_GAP,
-            new SequenceI[] { protein }, 4, 2, '-');
+            new SequenceI[]
+            { protein }, 4, 2, '-');
     ec.appendEdit(edit, prot, true, null);
 
     /*
@@ -861,34 +879,29 @@ public class MappingUtilsTest
   public void testFlattenRanges_reverseStrand()
   {
     assertEquals("[4, 3, 2, 1]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 1 })));
-    assertEquals(
-            "[4, 3, 2, 1]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 3, 2,
-                1 })));
-    assertEquals(
-            "[4, 3, 2, 1]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 4, 3,
-                3, 2, 2, 1, 1 })));
-    assertEquals(
-            "[12, 9, 8, 7, 4, 3, 2, 1]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 12, 12,
-                9, 7, 4, 1 })));
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 4, 1 })));
+    assertEquals("[4, 3, 2, 1]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 4, 3, 2, 1 })));
+    assertEquals("[4, 3, 2, 1]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 4, 4, 3, 3, 2, 2, 1, 1 })));
+    assertEquals("[12, 9, 8, 7, 4, 3, 2, 1]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 12, 12, 9, 7, 4, 1 })));
     // forwards and backwards anyone?
-    assertEquals(
-            "[4, 5, 6, 3, 2, 1]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 4, 6, 3,
-                1 })));
+    assertEquals("[4, 5, 6, 3, 2, 1]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 4, 6, 3, 1 })));
     // backwards and forwards
-    assertEquals(
-            "[3, 2, 1, 4, 5, 6]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 3, 1, 4,
-                6 })));
+    assertEquals("[3, 2, 1, 4, 5, 6]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 3, 1, 4, 6 })));
     // trailing unpaired start position is ignored:
-    assertEquals(
-            "[12, 9, 8, 7, 4, 3, 2]",
-            Arrays.toString(MappingUtils.flattenRanges(new int[] { 12, 12,
-                9, 7, 4, 2, 1 })));
+    assertEquals("[12, 9, 8, 7, 4, 3, 2]",
+            Arrays.toString(MappingUtils.flattenRanges(new int[]
+            { 12, 12, 9, 7, 4, 2, 1 })));
   }
 
   /**
@@ -1156,85 +1169,115 @@ public class MappingUtilsTest
     /*
      * both forward ranges
      */
-    assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        1, 10 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        2, 10 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        1, 9 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        4, 5 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        0, 9 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        -10, -9 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        1, 11 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        11, 12 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 1, 10 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 2, 10 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 1, 9 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 4, 5 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 0, 9 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { -10, -9 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 1, 11 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 11, 12 }));
 
     /*
      * forward range, reverse query
      */
-    assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        10, 1 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        9, 1 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        10, 2 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        5, 5 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        11, 1 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, new int[] {
-        10, 0 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 10, 1 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 9, 1 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 10, 2 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 5, 5 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 11, 1 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 10, 0 }));
 
     /*
      * reverse range, forward query
      */
-    assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        1, 10 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        1, 9 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        2, 10 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        6, 6 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        6, 11 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        11, 20 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        -3, -2 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 1, 10 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 1, 9 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 2, 10 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 6, 6 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 6, 11 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 11, 20 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { -3, -2 }));
 
     /*
      * both reverse
      */
-    assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        10, 1 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        9, 1 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        10, 2 }));
-    assertTrue(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        3, 3 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        11, 1 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        10, 0 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        12, 11 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 10, 1 }, new int[] {
-        -5, -8 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 10, 1 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 9, 1 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 10, 2 }));
+    assertTrue(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 3, 3 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 11, 1 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 10, 0 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { 12, 11 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 10, 1 }, new int[] { -5, -8 }));
 
     /*
      * bad arguments
      */
-    assertFalse(MappingUtils.rangeContains(new int[] { 1, 10, 12 },
-            new int[] {
-        1, 10 }));
-    assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 },
-            new int[] { 1 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10, 12 }, new int[] { 1, 10 }));
+    assertFalse(
+            MappingUtils.rangeContains(new int[]
+            { 1, 10 }, new int[] { 1 }));
     assertFalse(MappingUtils.rangeContains(new int[] { 1, 10 }, null));
     assertFalse(MappingUtils.rangeContains(null, new int[] { 1, 10 }));
   }
@@ -1284,4 +1327,33 @@ public class MappingUtilsTest
     assertEquals(1, ranges.size());
     assertEquals(9, ranges.get(0)[1]);
   }
+
+  @Test(groups = "Functional")
+  public void testListToArray()
+  {
+    List<int[]> ranges = new ArrayList<>();
+
+    int[] result = MappingUtils.rangeListToArray(ranges);
+    assertEquals(result.length, 0);
+    ranges.add(new int[] { 24, 12 });
+    result = MappingUtils.rangeListToArray(ranges);
+    assertEquals(result.length, 2);
+    assertEquals(result[0], 24);
+    assertEquals(result[1], 12);
+    ranges.add(new int[] { -7, 30 });
+    result = MappingUtils.rangeListToArray(ranges);
+    assertEquals(result.length, 4);
+    assertEquals(result[0], 24);
+    assertEquals(result[1], 12);
+    assertEquals(result[2], -7);
+    assertEquals(result[3], 30);
+    try
+    {
+      MappingUtils.rangeListToArray(null);
+      fail("Expected exception");
+    } catch (NullPointerException e)
+    {
+      // expected
+    }
+  }
 }
@@ -26,6 +26,7 @@ import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertSame;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.SequenceI;
@@ -40,9 +41,10 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-public class EmblSourceTest
+public class EmblXmlSourceTest
 {
 
   // adapted from http://www.ebi.ac.uk/ena/data/view/X07547&display=xml
@@ -95,16 +97,49 @@ public class EmblSourceTest
           + "ACCCCCAATATTGTGATATAATTAAAAACATAGCAT"
           + "</sequence></entry></ROOT>";
 
+  private EmblXmlSource testee;
+
+  @BeforeClass(alwaysRun = true)
+  public void setUp()
+  {
+    testee = new EmblXmlSource()
+    {
+
+      @Override
+      public String getDbSource()
+      {
+        return null;
+      }
+
+      @Override
+      public String getDbName()
+      {
+        return null;
+      }
+
+      @Override
+      public String getTestQuery()
+      {
+        return null;
+      }
+
+      @Override
+      public AlignmentI getSequenceRecords(String queries) throws Exception
+      {
+        return null;
+      }
+    };
+  }
+
   @Test(groups = "Functional")
   public void testGetCdsRanges()
   {
-    EmblSource testee = new EmblSource();
-
     /*
      * Make a (CDS) Feature with 5 locations
      */
     Feature cds = new Feature();
-    cds.setLocation("join(10..20,complement(30..40),50..60,70..80,complement(110..120))");
+    cds.setLocation(
+            "join(10..20,complement(30..40),50..60,70..80,complement(110..120))");
 
     int[] exons = testee.getCdsRanges("EMBL", cds);
     assertEquals("[10, 20, 40, 30, 50, 60, 70, 80, 120, 110]",
@@ -116,10 +151,9 @@ public class EmblSourceTest
   {
     // not the whole sequence but enough for this test...
     List<SequenceI> peptides = new ArrayList<>();
-    List<EntryType> entries = EmblSourceTest.getEmblEntries();
+    List<EntryType> entries = getEmblEntries();
     assertEquals(1, entries.size());
     EntryType entry = entries.get(0);
-    EmblSource testee = new EmblSource();
     String sourceDb = "EMBL";
     SequenceI dna = testee.getSequence(sourceDb, entry, peptides);
 
@@ -165,8 +199,9 @@ public class EmblSourceTest
             3, 1);
     MapList cds2Map = new MapList(new int[] { 4, 15 }, new int[] { 1, 4 },
             3, 1);
-    MapList cds3Map = new MapList(new int[] { 4, 6, 10, 15 }, new int[] {
-        1, 3 }, 3, 1);
+    MapList cds3Map = new MapList(new int[] { 4, 6, 10, 15 },
+            new int[]
+            { 1, 3 }, 3, 1);
 
     List<DBRefEntry> dbrefs = dna.getDBRefs();
     assertEquals(7, dbrefs.size());
@@ -222,10 +257,12 @@ public class EmblSourceTest
      * - to EMBLCDS (with 1:3 mapping)
      * - direct (no mapping) to other protein accessions
      */
-    MapList proteinToCdsMap1 = new MapList(new int[] { 1, 4 }, new int[] {
-        1, 12 }, 1, 3);
-    MapList proteinToCdsMap2 = new MapList(new int[] { 1, 3 }, new int[] {
-        1, 9 }, 1, 3);
+    MapList proteinToCdsMap1 = new MapList(new int[] { 1, 4 },
+            new int[]
+            { 1, 12 }, 1, 3);
+    MapList proteinToCdsMap2 = new MapList(new int[] { 1, 3 },
+            new int[]
+            { 1, 9 }, 1, 3);
 
     // dbrefs for first CDS EMBL product CAA30420.1
     dbrefs = peptides.get(0).getDBRefs();
@@ -339,10 +376,10 @@ public class EmblSourceTest
   @Test(groups = { "Functional" })
   public void testGetEmblEntries()
   {
-    List<EntryType> entries = EmblSourceTest.getEmblEntries();
+    List<EntryType> entries = getEmblEntries();
     assertEquals(1, entries.size());
     EntryType entry = entries.get(0);
-  
+
     assertEquals("X07547", entry.getAccession());
     assertEquals("C. trachomatis plasmid", entry.getDescription());
     assertEquals("STD", entry.getDataClass());
@@ -359,7 +396,7 @@ public class EmblSourceTest
     assertEquals(2, entry.getKeyword().size());
     assertEquals("plasmid", entry.getKeyword().get(0));
     assertEquals("unidentified reading frame", entry.getKeyword().get(1));
-  
+
     /*
      * dbrefs
      */
@@ -372,7 +409,7 @@ public class EmblSourceTest
     assertEquals("MD5", dbref.getDb());
     assertEquals("ac73317", dbref.getId());
     assertNull(dbref.getSecondaryId());
-  
+
     /*
      * three sequence features for CDS
      */
@@ -403,7 +440,7 @@ public class EmblSourceTest
     q = ef.getQualifier().get(2);
     assertEquals("translation", q.getName());
     assertEquals("MLCF", q.getValue());
-  
+
     /*
      * second CDS
      */
@@ -422,7 +459,7 @@ public class EmblSourceTest
     q = ef.getQualifier().get(1);
     assertEquals("translation", q.getName());
     assertEquals("MSSS", q.getValue());
-  
+
     /*
      * third CDS
      */
@@ -438,16 +475,14 @@ public class EmblSourceTest
     q = ef.getQualifier().get(1);
     assertEquals("translation", q.getName());
     assertEquals("MSS", q.getValue());
-  
+
     /*
      * Sequence - raw data before removal of newlines
      */
     String seq = entry.getSequence();
-    assertEquals(
-            "GGTATGTCCTCTAGTACAAAC\n"
-                    + "ACCCCCAATATTGTGATATAATTAAAAACATAGCAT",
-            seq);
-  
+    assertEquals("GGTATGTCCTCTAGTACAAAC\n"
+            + "ACCCCCAATATTGTGATATAATTAAAAACATAGCAT", seq);
+
     /*
      * getSequence() converts empty DBRefEntry.version to "0"
      */
@@ -455,9 +490,9 @@ public class EmblSourceTest
     assertNull(entry.getFeature().get(0).getXref().get(1).getSecondaryId());
   }
 
-  static List<EntryType> getEmblEntries()
+  List<EntryType> getEmblEntries()
   {
-    return new EmblSource()
+    return testee
             .getEmblEntries(new ByteArrayInputStream(TESTDATA.getBytes()));
   }
 }
index d8ed08e..8bfaef0 100644 (file)
  */
 package jalview.ws.dbsources;
 
+import static org.testng.Assert.assertFalse;
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertNull;
 import static org.testng.AssertJUnit.assertTrue;
 
+import jalview.datamodel.DBRefEntry;
+import jalview.datamodel.DBRefSource;
 import jalview.datamodel.SequenceI;
 import jalview.gui.JvOptionPane;
+import jalview.util.DBRefUtils;
 import jalview.xml.binding.uniprot.DbReferenceType;
 import jalview.xml.binding.uniprot.Entry;
 import jalview.xml.binding.uniprot.FeatureType;
@@ -218,6 +222,17 @@ public class UniprotTest
     assertEquals(6, seq.getDBRefs().size()); // 2*Uniprot, PDB, PDBsum, 2*EMBL
     assertEquals(seq.getSequenceAsString(),
             seq.createDatasetSequence().getSequenceAsString());
+    assertEquals(2,seq.getPrimaryDBRefs().size());
+    List<DBRefEntry> res = DBRefUtils.searchRefs(seq.getPrimaryDBRefs(), "A9CKP4");
+    assertEquals(1,res.size());
+    assertTrue(res.get(0).isCanonical());
+    res = DBRefUtils.searchRefsForSource(seq.getDBRefs(), DBRefSource.UNIPROT);
+    assertEquals(2,res.size());
+    /*
+     * NB this test fragile - relies on ordering being preserved
+     */
+    assertTrue(res.get(0).isCanonical());
+    assertFalse(res.get(1).isCanonical());
   }
 
   /**
index c559966..c3fae6c 100644 (file)
@@ -47,13 +47,13 @@ public class EBIFetchClientTest
     /*
      * EMBL
      */
-    assertEquals("https://www.ebi.ac.uk/ena/data/view/x53838&display=xml",
+    assertEquals("https://www.ebi.ac.uk/ena/browser/api/embl/x53838?download=true&gzip=true",
             EBIFetchClient.buildUrl("X53838", "EMBL", "display=xml"));
 
     /*
      * EMBLCDS
      */
-    assertEquals("https://www.ebi.ac.uk/ena/data/view/caa37824&display=xml",
+    assertEquals("https://www.ebi.ac.uk/ena/browser/api/embl/caa37824?download=true&gzip=true",
             EBIFetchClient.buildUrl("CAA37824", "EMBL", "display=xml"));
 
     /*
index 80b48c3..2318971 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.gui;
 
+import java.util.Locale;
+
 import jalview.bin.Cache;
 import jalview.gui.JvOptionPane;
 import jalview.gui.WsJobParameters;
@@ -66,7 +68,7 @@ public class Jws2ParamView
   public static List<String> presetTests = new ArrayList<String>();
   static
   {
-    serviceTests.add("AAConWS".toLowerCase());
+    serviceTests.add("AAConWS".toLowerCase(Locale.ROOT));
   }
 
   public static Jws2Discoverer disc = null;
@@ -91,7 +93,7 @@ public class Jws2ParamView
     for (Jws2Instance service : disc.getServices())
     {
       if (serviceTests.size() == 0
-              || serviceTests.contains(service.serviceType.toLowerCase()))
+              || serviceTests.contains(service.serviceType.toLowerCase(Locale.ROOT)))
       {
         List<Preset> prl = null;
         Preset pr = null;
index e8b6c2b..d13dc0f 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.jabaws;
 
+import java.util.Locale;
+
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 
@@ -85,7 +87,7 @@ public class DisorderAnnotExportImport
     iupreds = new ArrayList<Jws2Instance>();
     for (Jws2Instance svc : disc.getServices())
     {
-      if (svc.getServiceTypeURI().toLowerCase().contains("iupredws"))
+      if (svc.getServiceTypeURI().toLowerCase(Locale.ROOT).contains("iupredws"))
       {
         iupreds.add(svc);
       }
index 889c003..cc9aba0 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.jabaws;
 
+import java.util.Locale;
+
 import static org.testng.AssertJUnit.assertNotNull;
 import static org.testng.AssertJUnit.assertTrue;
 
@@ -98,7 +100,7 @@ public class RNAStructExportImport
     for (Jws2Instance svc : disc.getServices())
     {
 
-      if (svc.getServiceTypeURI().toLowerCase().contains("rnaalifoldws"))
+      if (svc.getServiceTypeURI().toLowerCase(Locale.ROOT).contains("rnaalifoldws"))
       {
         rnaalifoldws = svc;
       }
index c0aa2ee..51ff19c 100644 (file)
@@ -20,6 +20,8 @@
  */
 package jalview.ws.jws2;
 
+import java.util.Locale;
+
 import static org.testng.AssertJUnit.assertEquals;
 import static org.testng.AssertJUnit.assertFalse;
 import static org.testng.AssertJUnit.assertTrue;
@@ -68,7 +70,7 @@ public class ParameterUtilsTest
   @BeforeClass(alwaysRun = true)
   public static void setUpBeforeClass() throws Exception
   {
-    serviceTests.add("AAConWS".toLowerCase());
+    serviceTests.add("AAConWS".toLowerCase(Locale.ROOT));
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
     Cache.initLogger();
     disc = JalviewJabawsTestUtils.getJabawsDiscoverer();
@@ -131,7 +133,7 @@ public class ParameterUtilsTest
   public boolean isForTesting(Jws2Instance service)
   {
     return serviceTests.size() == 0
-            || serviceTests.contains(service.serviceType.toLowerCase());
+            || serviceTests.contains(service.serviceType.toLowerCase(Locale.ROOT));
   }
 
   @Test(groups = { "Network" })
index a7e5806..914520f 100644 (file)
@@ -30,10 +30,12 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.DBRefSource;
 import jalview.datamodel.FeatureProperties;
+import jalview.datamodel.Sequence;
 import jalview.datamodel.SequenceFeature;
 import jalview.datamodel.SequenceI;
 import jalview.gui.JvOptionPane;
 import jalview.util.DBRefUtils;
+import jalview.ws.DBRefFetcher;
 import jalview.ws.SequenceFetcher;
 import jalview.ws.dbsources.Pdb;
 import jalview.ws.dbsources.Uniprot;
@@ -77,6 +79,26 @@ public class DbRefFetcherTest
   {
   }
 
+  @Test(groups= {"Network"})
+  public void checkUniprotCanonicalFlagSet()
+  {
+    // TODO - mock this  - for moment it is a live request.
+    SequenceI uniprotSeq = new Sequence("FER1_SPIOL",
+            "MAATTTTMMGMATTFVPKPQAPPMMAALPSNTGRSLFGLKTGSRGGRMTMAAYKVTLVTPTGNVEFQCPDDV"
+            + "YILDAAEEEGIDLPYSCRAGSCSSCAGKLKTGSLNQDDQSFLDDDQIDEGWVLTCAAYPVSDVTIETHKEEE"
+            + "LTA");
+    DBRefFetcher dbr = new DBRefFetcher(new SequenceI[] { uniprotSeq });
+    dbr.fetchDBRefs(true);
+    List<DBRefEntry> primRefs = uniprotSeq.getPrimaryDBRefs();
+    assertNotNull(primRefs);
+    assertTrue(primRefs.size()>0);
+    boolean canonicalUp=false;
+    for (DBRefEntry ref:primRefs) {
+      assertEquals(DBRefSource.UNIPROT, ref.getCanonicalSourceName());
+      canonicalUp |= ref.isCanonical();
+    }
+    assertTrue("No Canonical Uniprot reference detected", canonicalUp);
+  }
   /**
    * Tests that standard protein database sources include Uniprot (as the first)
    * and also PDB. (Additional sources are dependent on availability of DAS
index 23f1c6e..c5c0f04 100644 (file)
@@ -15,6 +15,8 @@
  */
 package junit.extensions;
 
+import java.util.Locale;
+
 import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
@@ -432,7 +434,7 @@ final class PrivilegedAccessor
       {
         return false;
       }
-      if (className.startsWith(className.substring(0, 1).toUpperCase()))
+      if (className.startsWith(className.substring(0, 1).toUpperCase(Locale.ROOT)))
       {
         return true;
       }
index 4e6d27a..3e7e153 100644 (file)
@@ -16,3 +16,4 @@ uod_banner.28=/images/UoD_banner-28.png
 uod_banner.30=/images/UoD_banner-30.png
 uod_banner.32=/images/UoD_banner-32.png
 default_appbase=https://www.jalview.org/getdown/release/1.8
+preferences.filename=.jalview_properties
index 3407b22..6d5c6f7 100644 (file)
Binary files a/utils/channels/develop/images/jalview_develop_getdown_background.png and b/utils/channels/develop/images/jalview_develop_getdown_background.png differ
index 59c1abd..1fb439a 100644 (file)
Binary files a/utils/channels/develop/images/jalview_develop_getdown_background.xcf and b/utils/channels/develop/images/jalview_develop_getdown_background.xcf differ
index 9288e2d..0820e5f 100644 (file)
Binary files a/utils/channels/develop/images/jalview_develop_getdown_background_error.png and b/utils/channels/develop/images/jalview_develop_getdown_background_error.png differ
index 53cf561..ba09b29 100644 (file)
Binary files a/utils/channels/develop/images/jalview_develop_getdown_background_initialising.png and b/utils/channels/develop/images/jalview_develop_getdown_background_initialising.png differ
index fa848de..8af68fa 100644 (file)
@@ -16,3 +16,4 @@ uod_banner.28=/images/UoD_banner-28.png
 uod_banner.30=/images/UoD_banner-30.png
 uod_banner.32=/images/UoD_banner-32.png
 default_appbase=https://www.jalview.org/getdown/develop/11
+preferences.filename=.jalview_develop_properties
index 02e117d..8195dc8 100644 (file)
@@ -16,3 +16,4 @@ uod_banner.28=/images/UoD_banner-28.png
 uod_banner.30=/images/UoD_banner-30.png
 uod_banner.32=/images/UoD_banner-32.png
 default_appbase=https://www.jalview.org/getdown/release/1.8
+preferences.filename=.jalview_properties
index 75cd04d..8af46f4 100644 (file)
Binary files a/utils/channels/release/images/jalview_dmg_DS_Store and b/utils/channels/release/images/jalview_dmg_DS_Store differ
index c7f3dba..d2ebdec 100644 (file)
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        width="595.238px" height="595.238px" viewBox="0 0 595.238 595.238" enable-background="new 0 0 595.238 595.238"
-        xml:space="preserve">
-<rect x="234.925" y="234.464" fill="#0084A9" width="127.944" height="127.942"/>
-<polygon fill="#AD208E" points="395.955,38.359 38.788,38.359 38.823,362.389 103.187,362.389 103.187,102.72 460.325,102.72 "/>
-<polygon fill="#F78E1E" points="202.144,362.313 202.144,201.678 559.28,201.678 497.633,140.041 140.481,140.041 140.481,362.313 
-       "/>
-<polygon fill="#009DDC" points="202.225,558.595 560.882,558.607 560.848,234.574 496.501,234.574 496.501,494.26 137.861,494.236 
-       "/>
-<polygon fill="#C1D82F" points="395.688,234.646 395.688,395.282 38.904,395.282 100.535,456.929 457.329,456.929 457.329,234.646 
-       "/>
-<polygon fill="#008FC9" points="205.667,554.03 554.448,554.03 554.412,240.517 544.805,240.615 544.805,545.863 197.488,545.863 
-       "/>
-<polyline fill="#B0C62B" points="103.158,450.545 452.339,450.545 452.339,240.505 445.537,240.505 445.537,443.737 96.374,443.737 
-       "/>
-<polygon fill="#007799" points="356.649,240.339 241.236,240.339 241.236,356.261 247.337,356.261 247.337,246.708 356.649,246.708 
-       "/>
-<polygon fill="#0095B7" points="251.131,356.191 356.649,356.191 356.649,250.696 350.543,250.696 350.543,350.185 251.131,350.185 
-       "/>
-<polygon fill="#B956A0" points="97.55,356.417 97.55,96.444 446.327,96.444 436.58,86.163 87.278,86.163 87.278,356.347 "/>
-<polygon fill="#35ABE1" points="502.531,240.517 502.414,499.929 151.207,499.929 160.779,509.2 512.916,509.2 512.823,240.485 "/>
-<polygon fill="#CDDE5C" points="402.263,240.345 402.263,401.857 53.901,401.857 59.797,407.742 408.142,407.742 408.142,240.345 
-       "/>
-<polygon fill="#911076" points="53.112,356.377 53.112,52.218 404.342,52.102 395.792,43.38 43.473,43.363 43.504,356.377 "/>
-<polygon fill="#F9A24A" points="197.059,356.365 197.059,196.854 546.188,196.854 540.054,190.97 191.181,190.97 191.181,356.365 
-       "/>
-<polygon fill="#E4841C" points="155.107,356.365 155.2,153.2 502.403,153.2 495.595,146.38 147.283,146.38 147.283,356.365 "/>
-<path fill="none" stroke="#000000" stroke-width="0.1" d="M41.451,391.681"/>
-<path fill="none" stroke="#000000" stroke-width="0.1" d="M211.905,562.132"/>
-</svg>
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Layer_1"
+   x="0px"
+   y="0px"
+   width="522.09399"
+   height="522.09399"
+   viewBox="0 0 522.09402 522.09402"
+   enable-background="new 0 0 595.238 595.238"
+   xml:space="preserve"
+   sodipodi:docname="jalview_test-release_logo.svg"
+   inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"><metadata
+   id="metadata41"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+   id="defs39" /><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="2569"
+   inkscape:window-height="1721"
+   id="namedview37"
+   showgrid="false"
+   fit-margin-top="0"
+   fit-margin-left="0"
+   fit-margin-right="0"
+   fit-margin-bottom="0"
+   inkscape:zoom="2.24283"
+   inkscape:cx="258.83099"
+   inkscape:cy="260.988"
+   inkscape:window-x="0"
+   inkscape:window-y="0"
+   inkscape:window-maximized="0"
+   inkscape:current-layer="Layer_1" />
+<rect
+   x="196.13701"
+   y="197.95102"
+   width="127.944"
+   height="127.942"
+   id="rect2"
+   style="fill:#0084a9" />
+<polygon
+   points="38.823,362.389 103.187,362.389 103.187,102.72 460.325,102.72 395.955,38.359 38.788,38.359 "
+   id="polygon4"
+   style="fill:#ad208e"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="559.28,201.678 497.633,140.041 140.481,140.041 140.481,362.313 202.144,362.313 202.144,201.678 "
+   id="polygon6"
+   style="fill:#f78e1e"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="560.848,234.574 496.501,234.574 496.501,494.26 137.861,494.236 202.225,558.595 560.882,558.607 "
+   id="polygon8"
+   style="fill:#009ddc"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="38.904,395.282 100.535,456.929 457.329,456.929 457.329,234.646 395.688,234.646 395.688,395.282 "
+   id="polygon10"
+   style="fill:#c1d82f"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="554.412,240.517 544.805,240.615 544.805,545.863 197.488,545.863 205.667,554.03 554.448,554.03 "
+   id="polygon12"
+   style="fill:#008fc9"
+   transform="translate(-38.788,-36.512996)" />
+<polyline
+   points="103.158,450.545 452.339,450.545 452.339,240.505 445.537,240.505 445.537,443.737 96.374,443.737   "
+   id="polyline14"
+   style="fill:#b0c62b"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="241.236,356.261 247.337,356.261 247.337,246.708 356.649,246.708 356.649,240.339 241.236,240.339 "
+   id="polygon16"
+   style="fill:#007799"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="356.649,250.696 350.543,250.696 350.543,350.185 251.131,350.185 251.131,356.191 356.649,356.191 "
+   id="polygon18"
+   style="fill:#0095b7"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="446.327,96.444 436.58,86.163 87.278,86.163 87.278,356.347 97.55,356.417 97.55,96.444 "
+   id="polygon20"
+   style="fill:#b956a0"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="151.207,499.929 160.779,509.2 512.916,509.2 512.823,240.485 502.531,240.517 502.414,499.929 "
+   id="polygon22"
+   style="fill:#35abe1"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="53.901,401.857 59.797,407.742 408.142,407.742 408.142,240.345 402.263,240.345 402.263,401.857 "
+   id="polygon24"
+   style="fill:#cdde5c"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="404.342,52.102 395.792,43.38 43.473,43.363 43.504,356.377 53.112,356.377 53.112,52.218 "
+   id="polygon26"
+   style="fill:#911076"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="546.188,196.854 540.054,190.97 191.181,190.97 191.181,356.365 197.059,356.365 197.059,196.854 "
+   id="polygon28"
+   style="fill:#f9a24a"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="502.403,153.2 495.595,146.38 147.283,146.38 147.283,356.365 155.107,356.365 155.2,153.2 "
+   id="polygon30"
+   style="fill:#e4841c"
+   transform="translate(-38.788,-36.512996)" />
+<path
+   d="M 2.663,355.168"
+   id="path32"
+   inkscape:connector-curvature="0"
+   style="fill:none;stroke:#000000;stroke-width:0.1" />
+
+</svg>
\ No newline at end of file
index d19b281..abdfd3f 100644 (file)
@@ -16,3 +16,4 @@ uod_banner.28=/images/UoD_banner-28.png
 uod_banner.30=/images/UoD_banner-30.png
 uod_banner.32=/images/UoD_banner-32.png
 default_appbase=https://www.jalview.org/getdown/release/1.8
+preferences.filename=.jalview_properties
index cd9bf74..8247729 100644 (file)
@@ -6,16 +6,16 @@ getdown_background_image = jalview_test-release_getdown_background.png
 getdown_instant_background_image = jalview_test-release_getdown_background_initialising.png
 getdown_error_background = jalview_test-release_getdown_background_error.png
 getdown_progress_image = jalview_getdown_progress_bar.png
-getdown_mac_dock_icon = jalview_logo.icns
-getdown_icon = jalview_logo.png
+getdown_mac_dock_icon = jalview_test-release_logo.icns
+getdown_icon = jalview_test-release_logo.png
 getdown_txt_allow_offline = true
 getdown_txt_max_concurrent_downloads = 10
 getdown_txt_ui.install_error = https://www.jalview.org/faq/getdownerror
 getdown_txt_ui.hide_decorations = true
 install4j_images_dir = utils/channels/test-release/images
-install4j_mac_icons_file = jalview_logo.icns
-install4j_windows_icons_file = jalview_logo.ico
-install4j_png_icon_file = jalview_logo.png
+install4j_mac_icons_file = jalview_test-release_logo.icns
+install4j_windows_icons_file = jalview_test-release_logo.ico
+install4j_png_icon_file = jalview_test-release_logo.png
 install4j_background = jalview_logo_background_fade-640x480.png
 install4j_dmg_background = jalview_test-release_dmg_background-72dpi.png
 install4j_dmg_ds_store = jalview_test-release_dmg_DS_Store
diff --git a/utils/channels/test-release/images/jalview_logo.icns b/utils/channels/test-release/images/jalview_logo.icns
deleted file mode 100644 (file)
index 7f03345..0000000
Binary files a/utils/channels/test-release/images/jalview_logo.icns and /dev/null differ
diff --git a/utils/channels/test-release/images/jalview_logo.ico b/utils/channels/test-release/images/jalview_logo.ico
deleted file mode 100644 (file)
index 1fe7f0f..0000000
Binary files a/utils/channels/test-release/images/jalview_logo.ico and /dev/null differ
diff --git a/utils/channels/test-release/images/jalview_logo.svg b/utils/channels/test-release/images/jalview_logo.svg
deleted file mode 100644 (file)
index c7f3dba..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-        width="595.238px" height="595.238px" viewBox="0 0 595.238 595.238" enable-background="new 0 0 595.238 595.238"
-        xml:space="preserve">
-<rect x="234.925" y="234.464" fill="#0084A9" width="127.944" height="127.942"/>
-<polygon fill="#AD208E" points="395.955,38.359 38.788,38.359 38.823,362.389 103.187,362.389 103.187,102.72 460.325,102.72 "/>
-<polygon fill="#F78E1E" points="202.144,362.313 202.144,201.678 559.28,201.678 497.633,140.041 140.481,140.041 140.481,362.313 
-       "/>
-<polygon fill="#009DDC" points="202.225,558.595 560.882,558.607 560.848,234.574 496.501,234.574 496.501,494.26 137.861,494.236 
-       "/>
-<polygon fill="#C1D82F" points="395.688,234.646 395.688,395.282 38.904,395.282 100.535,456.929 457.329,456.929 457.329,234.646 
-       "/>
-<polygon fill="#008FC9" points="205.667,554.03 554.448,554.03 554.412,240.517 544.805,240.615 544.805,545.863 197.488,545.863 
-       "/>
-<polyline fill="#B0C62B" points="103.158,450.545 452.339,450.545 452.339,240.505 445.537,240.505 445.537,443.737 96.374,443.737 
-       "/>
-<polygon fill="#007799" points="356.649,240.339 241.236,240.339 241.236,356.261 247.337,356.261 247.337,246.708 356.649,246.708 
-       "/>
-<polygon fill="#0095B7" points="251.131,356.191 356.649,356.191 356.649,250.696 350.543,250.696 350.543,350.185 251.131,350.185 
-       "/>
-<polygon fill="#B956A0" points="97.55,356.417 97.55,96.444 446.327,96.444 436.58,86.163 87.278,86.163 87.278,356.347 "/>
-<polygon fill="#35ABE1" points="502.531,240.517 502.414,499.929 151.207,499.929 160.779,509.2 512.916,509.2 512.823,240.485 "/>
-<polygon fill="#CDDE5C" points="402.263,240.345 402.263,401.857 53.901,401.857 59.797,407.742 408.142,407.742 408.142,240.345 
-       "/>
-<polygon fill="#911076" points="53.112,356.377 53.112,52.218 404.342,52.102 395.792,43.38 43.473,43.363 43.504,356.377 "/>
-<polygon fill="#F9A24A" points="197.059,356.365 197.059,196.854 546.188,196.854 540.054,190.97 191.181,190.97 191.181,356.365 
-       "/>
-<polygon fill="#E4841C" points="155.107,356.365 155.2,153.2 502.403,153.2 495.595,146.38 147.283,146.38 147.283,356.365 "/>
-<path fill="none" stroke="#000000" stroke-width="0.1" d="M41.451,391.681"/>
-<path fill="none" stroke="#000000" stroke-width="0.1" d="M211.905,562.132"/>
-</svg>
index b7ea557..a5b6be1 100644 (file)
Binary files a/utils/channels/test-release/images/jalview_test-release_banner.xcf and b/utils/channels/test-release/images/jalview_test-release_banner.xcf differ
index 1603317..570778e 100644 (file)
Binary files a/utils/channels/test-release/images/jalview_test-release_dmg_DS_Store and b/utils/channels/test-release/images/jalview_test-release_dmg_DS_Store differ
index e73f842..ba614d9 100644 (file)
Binary files a/utils/channels/test-release/images/jalview_test-release_getdown_background.png and b/utils/channels/test-release/images/jalview_test-release_getdown_background.png differ
similarity index 98%
rename from utils/channels/test-release/images/jalview_getdown_background.xcf
rename to utils/channels/test-release/images/jalview_test-release_getdown_background.xcf
index 8cabadd..4f0f355 100644 (file)
Binary files a/utils/channels/test-release/images/jalview_getdown_background.xcf and b/utils/channels/test-release/images/jalview_test-release_getdown_background.xcf differ
index 05b2a24..cb34bce 100644 (file)
Binary files a/utils/channels/test-release/images/jalview_test-release_getdown_background_error.png and b/utils/channels/test-release/images/jalview_test-release_getdown_background_error.png differ
index 0d6e554..fa24e1f 100644 (file)
Binary files a/utils/channels/test-release/images/jalview_test-release_getdown_background_initialising.png and b/utils/channels/test-release/images/jalview_test-release_getdown_background_initialising.png differ
diff --git a/utils/channels/test-release/images/jalview_test-release_logo-84.png b/utils/channels/test-release/images/jalview_test-release_logo-84.png
new file mode 100644 (file)
index 0000000..57ee645
Binary files /dev/null and b/utils/channels/test-release/images/jalview_test-release_logo-84.png differ
diff --git a/utils/channels/test-release/images/jalview_test-release_logo-88.png b/utils/channels/test-release/images/jalview_test-release_logo-88.png
new file mode 100644 (file)
index 0000000..15c2111
Binary files /dev/null and b/utils/channels/test-release/images/jalview_test-release_logo-88.png differ
diff --git a/utils/channels/test-release/images/jalview_test-release_logo.icns b/utils/channels/test-release/images/jalview_test-release_logo.icns
new file mode 100644 (file)
index 0000000..ac3b0a6
Binary files /dev/null and b/utils/channels/test-release/images/jalview_test-release_logo.icns differ
diff --git a/utils/channels/test-release/images/jalview_test-release_logo.ico b/utils/channels/test-release/images/jalview_test-release_logo.ico
new file mode 100644 (file)
index 0000000..c86bbac
Binary files /dev/null and b/utils/channels/test-release/images/jalview_test-release_logo.ico differ
diff --git a/utils/channels/test-release/images/jalview_test-release_logo.png b/utils/channels/test-release/images/jalview_test-release_logo.png
new file mode 100644 (file)
index 0000000..623ffaa
Binary files /dev/null and b/utils/channels/test-release/images/jalview_test-release_logo.png differ
diff --git a/utils/channels/test-release/images/jalview_test-release_logo.svg b/utils/channels/test-release/images/jalview_test-release_logo.svg
new file mode 100644 (file)
index 0000000..f9b05b8
--- /dev/null
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Layer_1"
+   x="0px"
+   y="0px"
+   width="522.09399"
+   height="522.09399"
+   viewBox="0 0 522.09402 522.09402"
+   enable-background="new 0 0 595.238 595.238"
+   xml:space="preserve"
+   sodipodi:docname="jalview_test-release_logo.svg"
+   inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
+   inkscape:export-filename="/home/bsoares/Work/git/develop/jalview/utils/channels/tmp/jalview_test-release_logo-512.png"
+   inkscape:export-xdpi="94.139999"
+   inkscape:export-ydpi="94.139999"><metadata
+   id="metadata41"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+   id="defs39" /><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="2569"
+   inkscape:window-height="1721"
+   id="namedview37"
+   showgrid="false"
+   fit-margin-top="0"
+   fit-margin-left="0"
+   fit-margin-right="0"
+   fit-margin-bottom="0"
+   inkscape:zoom="0.56070751"
+   inkscape:cx="258.83099"
+   inkscape:cy="260.988"
+   inkscape:window-x="0"
+   inkscape:window-y="0"
+   inkscape:window-maximized="0"
+   inkscape:current-layer="Layer_1" />
+<rect
+   x="196.13701"
+   y="197.95102"
+   width="127.944"
+   height="127.942"
+   id="rect2"
+   style="fill:#ffffff;fill-opacity:1" />
+<polygon
+   points="38.823,362.389 103.187,362.389 103.187,102.72 460.325,102.72 395.955,38.359 38.788,38.359 "
+   id="polygon4"
+   style="fill:#ad208e"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="559.28,201.678 497.633,140.041 140.481,140.041 140.481,362.313 202.144,362.313 202.144,201.678 "
+   id="polygon6"
+   style="fill:#f78e1e"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="560.848,234.574 496.501,234.574 496.501,494.26 137.861,494.236 202.225,558.595 560.882,558.607 "
+   id="polygon8"
+   style="fill:#009ddc"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="38.904,395.282 100.535,456.929 457.329,456.929 457.329,234.646 395.688,234.646 395.688,395.282 "
+   id="polygon10"
+   style="fill:#c1d82f"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="554.412,240.517 544.805,240.615 544.805,545.863 197.488,545.863 205.667,554.03 554.448,554.03 "
+   id="polygon12"
+   style="fill:#008fc9"
+   transform="translate(-38.788,-36.512996)" />
+<polyline
+   points="103.158,450.545 452.339,450.545 452.339,240.505 445.537,240.505 445.537,443.737 96.374,443.737   "
+   id="polyline14"
+   style="fill:#b0c62b"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="241.236,356.261 247.337,356.261 247.337,246.708 356.649,246.708 356.649,240.339 241.236,240.339 "
+   id="polygon16"
+   style="fill:#d4d4d4;fill-opacity:1"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="356.649,250.696 350.543,250.696 350.543,350.185 251.131,350.185 251.131,356.191 356.649,356.191 "
+   id="polygon18"
+   style="fill:#f1f1f1;fill-opacity:1"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="446.327,96.444 436.58,86.163 87.278,86.163 87.278,356.347 97.55,356.417 97.55,96.444 "
+   id="polygon20"
+   style="fill:#b956a0"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="151.207,499.929 160.779,509.2 512.916,509.2 512.823,240.485 502.531,240.517 502.414,499.929 "
+   id="polygon22"
+   style="fill:#35abe1"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="53.901,401.857 59.797,407.742 408.142,407.742 408.142,240.345 402.263,240.345 402.263,401.857 "
+   id="polygon24"
+   style="fill:#cdde5c"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="404.342,52.102 395.792,43.38 43.473,43.363 43.504,356.377 53.112,356.377 53.112,52.218 "
+   id="polygon26"
+   style="fill:#911076"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="546.188,196.854 540.054,190.97 191.181,190.97 191.181,356.365 197.059,356.365 197.059,196.854 "
+   id="polygon28"
+   style="fill:#f9a24a"
+   transform="translate(-38.788,-36.512996)" />
+<polygon
+   points="502.403,153.2 495.595,146.38 147.283,146.38 147.283,356.365 155.107,356.365 155.2,153.2 "
+   id="polygon30"
+   style="fill:#e4841c"
+   transform="translate(-38.788,-36.512996)" />
+<path
+   d="M 2.663,355.168"
+   id="path32"
+   inkscape:connector-curvature="0"
+   style="fill:none;stroke:#000000;stroke-width:0.1" />
+
+</svg>
\ No newline at end of file
index 3ef86d3..e337bf8 100644 (file)
@@ -1,18 +1,19 @@
 app_name=Jalview Test
 banner=/images/jalview_test-release_banner.png
-logo.16=/images/jalview_logo-16.png
-logo.32=/images/jalview_logo-32.png
-logo.38=/images/jalview_logo-38.png
-logo.48=/images/jalview_logo-48.png
-logo.64=/images/jalview_logo-64.png
-logo.128=/images/jalview_logo-128.png
-logo.256=/images/jalview_logo-256.png
-logo.512=/images/jalview_logo-512.png
-rotatable_logo.48=/images/rotatable_jalview_logo-38.png
+logo.16=/images/jalview_test-release_logo-16.png
+logo.32=/images/jalview_test-release_logo-32.png
+logo.38=/images/jalview_test-release_logo-38.png
+logo.48=/images/jalview_test-release_logo-48.png
+logo.64=/images/jalview_test-release_logo-64.png
+logo.128=/images/jalview_test-release_logo-128.png
+logo.256=/images/jalview_test-release_logo-256.png
+logo.512=/images/jalview_test-release_logo-512.png
+rotatable_logo.48=/images/rotatable_jalview_test-release_logo-38.png
 bg_logo.28=/images/barton_group-28.png
 bg_logo.30=/images/barton_group-30.png
 bg_logo.32=/images/barton_group-32.png
 uod_banner.28=/images/UoD_banner-28.png
 uod_banner.30=/images/UoD_banner-30.png
 uod_banner.32=/images/UoD_banner-32.png
-default_appbase=https://www.jalview.org/getdown/release/1.8
+default_appbase=https://www.jalview.org/getdown/test-release/1.8
+preferences.filename=.jalview_test_properties
diff --git a/utils/channels/test-release/resources/images/jalview_logo-128.png b/utils/channels/test-release/resources/images/jalview_logo-128.png
deleted file mode 100644 (file)
index 63fa253..0000000
Binary files a/utils/channels/test-release/resources/images/jalview_logo-128.png and /dev/null differ
diff --git a/utils/channels/test-release/resources/images/jalview_logo-16.png b/utils/channels/test-release/resources/images/jalview_logo-16.png
deleted file mode 100644 (file)
index 9a1adfb..0000000
Binary files a/utils/channels/test-release/resources/images/jalview_logo-16.png and /dev/null differ
diff --git a/utils/channels/test-release/resources/images/jalview_logo-256.png b/utils/channels/test-release/resources/images/jalview_logo-256.png
deleted file mode 100644 (file)
index 6963612..0000000
Binary files a/utils/channels/test-release/resources/images/jalview_logo-256.png and /dev/null differ
diff --git a/utils/channels/test-release/resources/images/jalview_logo-32.png b/utils/channels/test-release/resources/images/jalview_logo-32.png
deleted file mode 100644 (file)
index 7af5791..0000000
Binary files a/utils/channels/test-release/resources/images/jalview_logo-32.png and /dev/null differ
diff --git a/utils/channels/test-release/resources/images/jalview_logo-38.png b/utils/channels/test-release/resources/images/jalview_logo-38.png
deleted file mode 100644 (file)
index 0b60196..0000000
Binary files a/utils/channels/test-release/resources/images/jalview_logo-38.png and /dev/null differ
diff --git a/utils/channels/test-release/resources/images/jalview_logo-48.png b/utils/channels/test-release/resources/images/jalview_logo-48.png
deleted file mode 100644 (file)
index 297e6cc..0000000
Binary files a/utils/channels/test-release/resources/images/jalview_logo-48.png and /dev/null differ
diff --git a/utils/channels/test-release/resources/images/jalview_logo-512.png b/utils/channels/test-release/resources/images/jalview_logo-512.png
deleted file mode 100644 (file)
index 340f8e5..0000000
Binary files a/utils/channels/test-release/resources/images/jalview_logo-512.png and /dev/null differ
diff --git a/utils/channels/test-release/resources/images/jalview_logo-64.png b/utils/channels/test-release/resources/images/jalview_logo-64.png
deleted file mode 100644 (file)
index 2505ae9..0000000
Binary files a/utils/channels/test-release/resources/images/jalview_logo-64.png and /dev/null differ
index e4aed51..5ba07b8 100644 (file)
Binary files a/utils/channels/test-release/resources/images/jalview_test-release_banner.png and b/utils/channels/test-release/resources/images/jalview_test-release_banner.png differ
diff --git a/utils/channels/test-release/resources/images/jalview_test-release_logo-128.png b/utils/channels/test-release/resources/images/jalview_test-release_logo-128.png
new file mode 100644 (file)
index 0000000..5e351f8
Binary files /dev/null and b/utils/channels/test-release/resources/images/jalview_test-release_logo-128.png differ
diff --git a/utils/channels/test-release/resources/images/jalview_test-release_logo-16.png b/utils/channels/test-release/resources/images/jalview_test-release_logo-16.png
new file mode 100644 (file)
index 0000000..6c398dd
Binary files /dev/null and b/utils/channels/test-release/resources/images/jalview_test-release_logo-16.png differ
diff --git a/utils/channels/test-release/resources/images/jalview_test-release_logo-256.png b/utils/channels/test-release/resources/images/jalview_test-release_logo-256.png
new file mode 100644 (file)
index 0000000..7f9af58
Binary files /dev/null and b/utils/channels/test-release/resources/images/jalview_test-release_logo-256.png differ
diff --git a/utils/channels/test-release/resources/images/jalview_test-release_logo-32.png b/utils/channels/test-release/resources/images/jalview_test-release_logo-32.png
new file mode 100644 (file)
index 0000000..ecc0a38
Binary files /dev/null and b/utils/channels/test-release/resources/images/jalview_test-release_logo-32.png differ
diff --git a/utils/channels/test-release/resources/images/jalview_test-release_logo-38.png b/utils/channels/test-release/resources/images/jalview_test-release_logo-38.png
new file mode 100644 (file)
index 0000000..5438d4e
Binary files /dev/null and b/utils/channels/test-release/resources/images/jalview_test-release_logo-38.png differ
diff --git a/utils/channels/test-release/resources/images/jalview_test-release_logo-48.png b/utils/channels/test-release/resources/images/jalview_test-release_logo-48.png
new file mode 100644 (file)
index 0000000..108f927
Binary files /dev/null and b/utils/channels/test-release/resources/images/jalview_test-release_logo-48.png differ
diff --git a/utils/channels/test-release/resources/images/jalview_test-release_logo-512.png b/utils/channels/test-release/resources/images/jalview_test-release_logo-512.png
new file mode 100644 (file)
index 0000000..623ffaa
Binary files /dev/null and b/utils/channels/test-release/resources/images/jalview_test-release_logo-512.png differ
diff --git a/utils/channels/test-release/resources/images/jalview_test-release_logo-64.png b/utils/channels/test-release/resources/images/jalview_test-release_logo-64.png
new file mode 100644 (file)
index 0000000..a599ba3
Binary files /dev/null and b/utils/channels/test-release/resources/images/jalview_test-release_logo-64.png differ
diff --git a/utils/channels/test-release/resources/images/rotatable_jalview_logo-38.png b/utils/channels/test-release/resources/images/rotatable_jalview_logo-38.png
deleted file mode 100644 (file)
index e584298..0000000
Binary files a/utils/channels/test-release/resources/images/rotatable_jalview_logo-38.png and /dev/null differ
diff --git a/utils/channels/test-release/resources/images/rotatable_jalview_test-release_logo-38.png b/utils/channels/test-release/resources/images/rotatable_jalview_test-release_logo-38.png
new file mode 100644 (file)
index 0000000..53b6ec7
Binary files /dev/null and b/utils/channels/test-release/resources/images/rotatable_jalview_test-release_logo-38.png differ
diff --git a/utils/create_iconfiles.sh b/utils/create_iconfiles.sh
deleted file mode 100755 (executable)
index 241c195..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-
-# given a list of differently sized png files with the size in the filename like this:
-# myiconfile-16.png myiconfile-32.png myiconfile-48.png myiconfile-64.png myiconfile-128.png myiconfile-256.png myiconfile-512.png
-# run
-# create_iconfiles.sh myiconfile-*.png
-# to create an ICO file (for Windows) myiconfile.ico and an ICNS file (for macOS) myiconfile.icns
-
-BASENAME=${1%-*}
-if [ x$BASENAME = x ]; then
-  echo "Could not calculate basename from '${1}'"
-  exit 1
-fi
-
-ICOFILE=${BASENAME}.ico
-echo "Creating ${ICOFILE}"
-convert $* ${ICOFILE}
-
-ICNSFILE=${BASENAME}.icns
-echo "Creating ${ICNSFILE}"
-# dont' include 64x64 icon (for some reason they're not allowed in icns file)
-ICNSARGS=""
-for ARG in $*; do
-  NUM=${ARG##*-}
-  NUM=${NUM%px*}
-  NUM=${NUM%x*}
-  NUM=${NUM%.*}
-  if [ x$NUM != x64 -a x$NUM != x38 ]; then
-    ICNSARGS="${ICNSARGS} $ARG"
-  fi
-done
-png2icns ${ICNSFILE} ${ICNSARGS}
diff --git a/utils/debian/build_gradle.patch b/utils/debian/build_gradle.patch
new file mode 100644 (file)
index 0000000..1527c79
--- /dev/null
@@ -0,0 +1,2909 @@
+--- a/build.gradle     2021-09-21 09:52:04.653972716 +0100
++++ b/build.gradle     2021-09-21 09:52:18.117985307 +0100
+@@ -2,56 +2,12 @@
+  * For properties set within build.gradle, use camelCaseNoSpace.
+  */
+ import org.apache.tools.ant.filters.ReplaceTokens
+-import org.gradle.internal.os.OperatingSystem
+-import org.gradle.plugins.ide.internal.generator.PropertiesPersistableConfigurationObject
+-import org.gradle.api.internal.PropertiesTransformer
+-import org.gradle.util.ConfigureUtil
+-import org.gradle.plugins.ide.eclipse.model.Output
+-import org.gradle.plugins.ide.eclipse.model.Library
+-import java.security.MessageDigest
+-import groovy.transform.ExternalizeMethods
+-import groovy.util.XmlParser
+-import groovy.xml.XmlUtil
+-import com.vladsch.flexmark.util.ast.Node
+-import com.vladsch.flexmark.html.HtmlRenderer
+-import com.vladsch.flexmark.parser.Parser
+-import com.vladsch.flexmark.util.data.MutableDataSet
+-import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension
+-import com.vladsch.flexmark.ext.tables.TablesExtension
+-import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension
+-import com.vladsch.flexmark.ext.autolink.AutolinkExtension
+-import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension
+-import com.vladsch.flexmark.ext.toc.TocExtension
+-
+-buildscript {
+-  repositories {
+-    mavenCentral()
+-    mavenLocal()
+-  }
+-  dependencies {
+-    classpath "com.vladsch.flexmark:flexmark-all:0.62.0"
+-  }
+-}
+-
+ plugins {
+   id 'java'
+   id 'application'
+-  id 'eclipse'
+-  id "com.diffplug.gradle.spotless" version "3.28.0"
+-  id 'com.github.johnrengelman.shadow' version '4.0.3'
+-  id 'com.install4j.gradle' version '8.0.10'
+-  id 'com.dorongold.task-tree' version '1.5' // only needed to display task dependency tree with  gradle task1 [task2 ...] taskTree
+-  id 'com.palantir.git-version' version '0.12.3'
+-}
+-
+-repositories {
+-  jcenter()
+-  mavenCentral()
+-  mavenLocal()
+ }
+-
+ // in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
+ def string(Object o) {
+   return o == null ? "" : o.toString()
+@@ -92,23 +48,15 @@
+   }
+ }
+-ext {
++project.ext {
+   jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
+   jalviewDirRelativePath = jalviewDir
+-  getdownChannelName = CHANNEL.toLowerCase()
+-  // default to "default". Currently only has different cosmetics for "develop", "release", "default"
+-  propertiesChannelName = ["develop", "release", "test-release", "jalviewjs", "jalviewjs-release" ].contains(getdownChannelName) ? getdownChannelName : "default"
+-  // Import channel_properties
++  propertiesChannelName = "release"
+   channelDir = string("${jalviewDir}/${channel_properties_dir}/${propertiesChannelName}")
+   channelGradleProperties = string("${channelDir}/channel_gradle.properties")
+   overrideProperties(channelGradleProperties, false)
+-  // local build environment properties
+-  // can be "projectDir/local.properties"
+-  overrideProperties("${projectDir}/local.properties", true)
+-  // or "../projectDir_local.properties"
+-  overrideProperties(projectDir.getParent() + "/" + projectDir.getName() + "_local.properties", true)
+-
++  
+   ////  
+   // Import releaseProps from the RELEASE file
+   // or a file specified via JALVIEW_RELEASE_FILE if defined
+@@ -128,41 +76,6 @@
+   if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
+     JALVIEW_VERSION = releaseProps.get("jalview.version")
+   }
+-  
+-  // this property set when running Eclipse headlessly
+-  j2sHeadlessBuildProperty = string("net.sf.j2s.core.headlessbuild")
+-  // this property set by Eclipse
+-  eclipseApplicationProperty = string("eclipse.application")
+-  // CHECK IF RUNNING FROM WITHIN ECLIPSE
+-  def eclipseApplicationPropertyVal = System.properties[eclipseApplicationProperty]
+-  IN_ECLIPSE = eclipseApplicationPropertyVal != null && eclipseApplicationPropertyVal.startsWith("org.eclipse.ui.")
+-  // BUT WITHOUT THE HEADLESS BUILD PROPERTY SET
+-  if (System.properties[j2sHeadlessBuildProperty].equals("true")) {
+-    println("Setting IN_ECLIPSE to ${IN_ECLIPSE} as System.properties['${j2sHeadlessBuildProperty}'] == '${System.properties[j2sHeadlessBuildProperty]}'")
+-    IN_ECLIPSE = false
+-  }
+-  if (IN_ECLIPSE) {
+-    println("WITHIN ECLIPSE IDE")
+-  } else {
+-    println("HEADLESS BUILD")
+-  }
+-  
+-  J2S_ENABLED = (project.hasProperty('j2s.compiler.status') && project['j2s.compiler.status'] != null && project['j2s.compiler.status'] == "enable")
+-  if (J2S_ENABLED) {
+-    println("J2S ENABLED")
+-  } 
+-  /* *-/
+-  System.properties.sort { it.key }.each {
+-    key, val -> println("SYSTEM PROPERTY ${key}='${val}'")
+-  }
+-  /-* *-/
+-  if (false && IN_ECLIPSE) {
+-    jalviewDir = jalviewDirAbsolutePath
+-  }
+-  */
+-
+-  // datestamp
+-  buildDate = new Date().format("yyyyMMdd")
+   // essentials
+   bareSourceDir = string(source_dir)
+@@ -173,218 +86,18 @@
+   classesDir = string("${jalviewDir}/${classes_dir}")
+-  // clover
+-  useClover = clover.equals("true")
+-  cloverBuildDir = "${buildDir}/clover"
+-  cloverInstrDir = file("${cloverBuildDir}/clover-instr")
+-  cloverClassesDir = file("${cloverBuildDir}/clover-classes")
+-  cloverReportDir = file("${buildDir}/reports/clover")
+-  cloverTestInstrDir = file("${cloverBuildDir}/clover-test-instr")
+-  cloverTestClassesDir = file("${cloverBuildDir}/clover-test-classes")
+-  //cloverTestClassesDir = cloverClassesDir
+-  cloverDb = string("${cloverBuildDir}/clover.db")
+-
+-  testSourceDir = useClover ? cloverTestInstrDir : testDir
+-  testClassesDir = useClover ? cloverTestClassesDir : "${jalviewDir}/${test_output_dir}"
+-
+-  getdownWebsiteDir = string("${jalviewDir}/${getdown_website_dir}/${JAVA_VERSION}")
+-  buildDist = true
+-  buildProperties = null
+-
+-  // the following values might be overridden by the CHANNEL switch
+-  getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
+-  getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
+-  getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher}")
+-  getdownAppDistDir = getdown_app_dir_alt
+-  getdownImagesDir = string("${jalviewDir}/${getdown_images_dir}")
+-  getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
+-  reportRsyncCommand = false
+-  jvlChannelName = CHANNEL.toLowerCase()
+-  install4jSuffix = CHANNEL.substring(0, 1).toUpperCase() + CHANNEL.substring(1).toLowerCase(); // BUILD -> Build
+-  install4jDMGDSStore = "${install4j_images_dir}/${install4j_dmg_ds_store}"
+-  install4jDMGBackgroundImage = "${install4j_images_dir}/${install4j_dmg_background}"
+-  install4jInstallerName = "${jalview_name} Non-Release Installer"
+-  install4jExecutableName = install4j_executable_name
+-  install4jExtraScheme = "jalviewx"
+-  install4jMacIconsFile = string("${install4j_images_dir}/${install4j_mac_icons_file}")
+-  install4jWindowsIconsFile = string("${install4j_images_dir}/${install4j_windows_icons_file}")
+-  install4jPngIconFile = string("${install4j_images_dir}/${install4j_png_icon_file}")
+-  install4jBackground = string("${install4j_images_dir}/${install4j_background}")
+-  switch (CHANNEL) {
+-
+-    case "BUILD":
+-    // TODO: get bamboo build artifact URL for getdown artifacts
+-    getdown_channel_base = bamboo_channelbase
+-    getdownChannelName = string("${bamboo_planKey}/${JAVA_VERSION}")
+-    getdownAppBase = string("${bamboo_channelbase}/${bamboo_planKey}${bamboo_getdown_channel_suffix}/${JAVA_VERSION}")
+-    jvlChannelName += "_${getdownChannelName}"
+-    // automatically add the test group Not-bamboo for exclusion 
+-    if ("".equals(testng_excluded_groups)) { 
+-      testng_excluded_groups = "Not-bamboo"
+-    }
+-    install4jExtraScheme = "jalviewb"
+-    break
++  useClover = false
+-    case [ "RELEASE", "JALVIEWJS-RELEASE" ]:
+-    getdownAppDistDir = getdown_app_dir_release
+-    reportRsyncCommand = true
+-    install4jSuffix = ""
+-    install4jInstallerName = "${jalview_name} Installer"
+-    break
+-
+-    case "ARCHIVE":
+-    getdownChannelName = CHANNEL.toLowerCase()+"/${JALVIEW_VERSION}"
+-    getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
+-    getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
+-    if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
+-      throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
+-    } else {
+-      package_dir = string("${ARCHIVEDIR}/${package_dir}")
+-      buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
+-      buildDist = false
+-    }
+-    reportRsyncCommand = true
+-    install4jExtraScheme = "jalviewa"
+-    break
+-
+-    case "ARCHIVELOCAL":
+-    getdownChannelName = string("archive/${JALVIEW_VERSION}")
+-    getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
+-    getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+-    if (!file("${ARCHIVEDIR}/${package_dir}").exists()) {
+-      throw new GradleException("Must provide an ARCHIVEDIR value to produce an archive distribution")
+-    } else {
+-      package_dir = string("${ARCHIVEDIR}/${package_dir}")
+-      buildProperties = string("${ARCHIVEDIR}/${classes_dir}/${build_properties_file}")
+-      buildDist = false
+-    }
+-    reportRsyncCommand = true
+-    getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
+-    install4jSuffix = "Archive"
+-    install4jExtraScheme = "jalviewa"
+-    break
+-
+-    case "DEVELOP":
+-    reportRsyncCommand = true
+-    getdownSetAppBaseProperty = true
+-    // DEVELOP-RELEASE is usually associated with a Jalview release series so set the version
+-    JALVIEW_VERSION=JALVIEW_VERSION+"-d${buildDate}"
+-    
+-    install4jSuffix = "Develop"
+-    install4jExtraScheme = "jalviewd"
+-    install4jInstallerName = "${jalview_name} Develop Installer"
+-    break
+-
+-    case "TEST-RELEASE":
+-    reportRsyncCommand = true
+-    // Don't ignore transpile errors for release build
+-    if (jalviewjs_ignore_transpile_errors.equals("true")) {
+-      jalviewjs_ignore_transpile_errors = "false"
+-      println("Setting jalviewjs_ignore_transpile_errors to 'false'")
+-    }
+-    JALVIEW_VERSION = JALVIEW_VERSION+"-test"
+-    install4jSuffix = "Test"
+-    install4jExtraScheme = "jalviewt"
+-    install4jInstallerName = "${jalview_name} Test Installer"
+-    break
+-
+-    case ~/^SCRATCH(|-[-\w]*)$/:
+-    getdownChannelName = CHANNEL
+-    JALVIEW_VERSION = JALVIEW_VERSION+"-"+CHANNEL
+-    
+-    getdownDir = string("${getdownChannelName}/${JAVA_VERSION}")
+-    getdownAppBase = string("${getdown_channel_base}/${getdownDir}")
+-    reportRsyncCommand = true
+-    install4jSuffix = "Scratch"
+-    break
+-
+-    case "TEST-LOCAL":
+-    if (!file("${LOCALDIR}").exists()) {
+-      throw new GradleException("Must provide a LOCALDIR value to produce a local distribution")
+-    } else {
+-      getdownAppBase = file(file("${LOCALDIR}").getAbsolutePath()).toURI().toString()
+-      getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
+-    }
+-    JALVIEW_VERSION = "TEST"
+-    install4jSuffix = "Test-Local"
+-    install4jExtraScheme = "jalviewt"
+-    install4jInstallerName = "${jalview_name} Test Installer"
+-    break
+-
+-    case [ "LOCAL", "JALVIEWJS" ]:
+-    JALVIEW_VERSION = "TEST"
+-    getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+-    getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
+-    install4jExtraScheme = "jalviewl"
+-    break
+-
+-    default: // something wrong specified
+-    throw new GradleException("CHANNEL must be one of BUILD, RELEASE, ARCHIVE, DEVELOP, TEST-RELEASE, SCRATCH-..., LOCAL [default]")
+-    break
+-
+-  }
+-  // override getdownAppBase if requested
+-  if (findProperty("getdown_appbase_override") != null) {
+-    // revert to LOCAL if empty string
+-    if (string(getdown_appbase_override) == "") {
+-      getdownAppBase = file(getdownWebsiteDir).toURI().toString()
+-      getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
+-    } else if (string(getdown_appbase_override).startsWith("file://")) {
+-      getdownAppBase = string(getdown_appbase_override)
+-      getdownLauncher = string("${jalviewDir}/${getdown_lib_dir}/${getdown_launcher_local}")
+-    } else {
+-      getdownAppBase = string(getdown_appbase_override)
+-    }
+-    println("Overriding getdown appbase with '${getdownAppBase}'")
+-  }
+-  // sanitise file name for jalview launcher file for this channel
+-  jvlChannelName = jvlChannelName.replaceAll("[^\\w\\-]+", "_")
+-  // install4j application and folder names
+-  if (install4jSuffix == "") {
+-    install4jApplicationName = "${jalview_name}"
+-    install4jBundleId = "${install4j_bundle_id}"
+-    install4jWinApplicationId = install4j_release_win_application_id
+-  } else {
+-    install4jApplicationName = "${jalview_name} ${install4jSuffix}"
+-    install4jBundleId = "${install4j_bundle_id}-" + install4jSuffix.toLowerCase()
+-    // add int hash of install4jSuffix to the last part of the application_id
+-    def id = install4j_release_win_application_id
+-    def idsplitreverse = id.split("-").reverse()
+-    idsplitreverse[0] = idsplitreverse[0].toInteger() + install4jSuffix.hashCode()
+-    install4jWinApplicationId = idsplitreverse.reverse().join("-")
+-  }
+-  // sanitise folder and id names
+-  // install4jApplicationFolder = e.g. "Jalview Build"
+-  install4jApplicationFolder = install4jApplicationName
+-                                    .replaceAll("[\"'~:/\\\\\\s]", "_") // replace all awkward filename chars " ' ~ : / \
+-                                    .replaceAll("_+", "_") // collapse __
+-  install4jInternalId = install4jApplicationName
+-                                    .replaceAll(" ","_")
+-                                    .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
+-                                    .replaceAll("_+", "") // collapse __
+-                                    //.replaceAll("_*-_*", "-") // collapse _-_
+-  install4jUnixApplicationFolder = install4jApplicationName
+-                                    .replaceAll(" ","_")
+-                                    .replaceAll("[^\\w\\-\\.]", "_") // replace other non [alphanumeric,_,-,.]
+-                                    .replaceAll("_+", "_") // collapse __
+-                                    .replaceAll("_*-_*", "-") // collapse _-_
+-                                    .toLowerCase()
+-
+-  getdownWrapperLink = install4jUnixApplicationFolder // e.g. "jalview_local"
+-  getdownAppDir = string("${getdownWebsiteDir}/${getdownAppDistDir}")
+-  //getdownJ11libDir = "${getdownWebsiteDir}/${getdown_j11lib_dir}"
+-  getdownResourceDir = string("${getdownWebsiteDir}/${getdown_resource_dir}")
+-  getdownInstallDir = string("${getdownWebsiteDir}/${getdown_install_dir}")
+-  getdownFilesDir = string("${jalviewDir}/${getdown_files_dir}/${JAVA_VERSION}/")
+-  getdownFilesInstallDir = string("${getdownFilesDir}/${getdown_install_dir}")
+-  /* compile without modules -- using classpath libraries
+-  modules_compileClasspath = fileTree(dir: "${jalviewDir}/${j11modDir}", include: ["*.jar"])
+-  modules_runtimeClasspath = modules_compileClasspath
+-  */
+-  def details = versionDetails()
+-  gitHash = details.gitHash
+-  gitBranch = details.branchName
++  resourceClassesDir = classesDir
++
++  testSourceDir = testDir
++  testClassesDir = "${jalviewDir}/${test_output_dir}"
++  buildProperties = string("${classesDir}/${build_properties_file}")
++  getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
++
++  install4jApplicationName = "${jalview_name}"
++  
+   println("Using a ${CHANNEL} profile.")
+   additional_compiler_args = []
+@@ -396,71 +109,16 @@
+     libDistDir = j8libDir
+     compile_source_compatibility = 1.8
+     compile_target_compatibility = 1.8
+-    // these are getdown.txt properties defined dependent on the JAVA_VERSION
+-    getdownAltJavaMinVersion = string(findProperty("getdown_alt_java8_min_version"))
+-    getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java8_max_version"))
+-    // this property is assigned below and expanded to multiple lines in the getdown task
+-    getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java8_txt_multi_java_location"))
+-    // this property is for the Java library used in eclipse
+-    eclipseJavaRuntimeName = string("JavaSE-1.8")
+   } else if (JAVA_VERSION.equals("11")) {
+     JAVA_INTEGER_VERSION = string("11")
+     libDir = j11libDir
+     libDistDir = j11libDir
+     compile_source_compatibility = 11
+     compile_target_compatibility = 11
+-    getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
+-    getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
+-    getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
+-    eclipseJavaRuntimeName = string("JavaSE-11")
+-    /* compile without modules -- using classpath libraries
+-    additional_compiler_args += [
+-    '--module-path', 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
+-    getdownAltJavaMinVersion = string(findProperty("getdown_alt_java11_min_version"))
+-    getdownAltJavaMaxVersion = string(findProperty("getdown_alt_java11_max_version"))
+-    getdownAltMultiJavaLocation = string(findProperty("getdown_alt_java11_txt_multi_java_location"))
+-    eclipseJavaRuntimeName = string("JavaSE-11")
+-    /* compile without modules -- using classpath libraries
+-    additional_compiler_args += [
+-    '--module-path', modules_compileClasspath.asPath,
+-    '--add-modules', j11modules
+-    ]
+-     */
+   } else {
+     throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
+   }
+-
+-  // for install4j
+-  JAVA_MIN_VERSION = JAVA_VERSION
+-  JAVA_MAX_VERSION = JAVA_VERSION
+-  def jreInstallsDir = string(jre_installs_dir)
+-  if (jreInstallsDir.startsWith("~/")) {
+-    jreInstallsDir = System.getProperty("user.home") + jreInstallsDir.substring(1)
+-  }
+-  macosJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-mac-x64/jre")
+-  macosJavaVMTgz = string("${jreInstallsDir}/tgz/jre-${JAVA_INTEGER_VERSION}-mac-x64.tar.gz")
+-  windowsJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-windows-x64/jre")
+-  windowsJavaVMTgz = string("${jreInstallsDir}/tgz/jre-${JAVA_INTEGER_VERSION}-windows-x64.tar.gz")
+-  linuxJavaVMDir = string("${jreInstallsDir}/jre-${JAVA_INTEGER_VERSION}-linux-x64/jre")
+-  linuxJavaVMTgz = string("${jreInstallsDir}/tgz/jre-${JAVA_INTEGER_VERSION}-linux-x64.tar.gz")
+-  install4jDir = string("${jalviewDir}/${install4j_utils_dir}")
+-  install4jConfFileName = string("jalview-install4j-conf.install4j")
+-  install4jConfFile = file("${install4jDir}/${install4jConfFileName}")
+-  install4jHomeDir = install4j_home_dir
+-  if (install4jHomeDir.startsWith("~/")) {
+-    install4jHomeDir = System.getProperty("user.home") + install4jHomeDir.substring(1)
+-  }
+-
+   resourceBuildDir = string("${buildDir}/resources")
+   resourcesBuildDir = string("${resourceBuildDir}/resources_build")
+   helpBuildDir = string("${resourceBuildDir}/help_build")
+@@ -474,31 +132,6 @@
+   helpSourceDir = string("${helpParentDir}/${help_dir}")
+   helpFile = string("${helpBuildDir}/${help_dir}/help.jhm")
+-
+-  relativeBuildDir = file(jalviewDirAbsolutePath).toPath().relativize(buildDir.toPath())
+-  jalviewjsBuildDir = string("${relativeBuildDir}/jalviewjs")
+-  jalviewjsSiteDir = string("${jalviewjsBuildDir}/${jalviewjs_site_dir}")
+-  if (IN_ECLIPSE) {
+-    jalviewjsTransferSiteJsDir = string(jalviewjsSiteDir)
+-  } else {
+-    jalviewjsTransferSiteJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_js")
+-  }
+-  jalviewjsTransferSiteLibDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_lib")
+-  jalviewjsTransferSiteSwingJsDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_swingjs")
+-  jalviewjsTransferSiteCoreDir = string("${jalviewjsBuildDir}/tmp/${jalviewjs_site_dir}_core")
+-  jalviewjsJalviewCoreHtmlFile = string("")
+-  jalviewjsJalviewCoreName = string(jalviewjs_core_name)
+-  jalviewjsCoreClasslists = []
+-  jalviewjsJalviewTemplateName = string(jalviewjs_name)
+-  jalviewjsJ2sSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_settings}")
+-  jalviewjsJ2sAltSettingsFileName = string("${jalviewDir}/${jalviewjs_j2s_alt_settings}")
+-  jalviewjsJ2sProps = null
+-  jalviewjsJ2sPlugin = jalviewjs_j2s_plugin
+-
+-  eclipseWorkspace = null
+-  eclipseBinary = string("")
+-  eclipseVersion = string("")
+-  eclipseDebug = false
+   // ENDEXT
+ }
+@@ -517,27 +150,12 @@
+     compileClasspath = files(sourceSets.main.java.outputDir)
+     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
+-    runtimeClasspath = compileClasspath
+-    runtimeClasspath += files(sourceSets.main.resources.srcDirs)
+-  }
+-
+-  clover {
+-    java {
+-      srcDirs cloverInstrDir
+-      outputDir = cloverClassesDir
+-    }
+-
+-    resources {
+-      srcDirs = sourceSets.main.resources.srcDirs
+-    }
+-    compileClasspath = files( sourceSets.clover.java.outputDir )
+-    //compileClasspath += files( testClassesDir )
++    compileClasspath = files(sourceSets.main.java.outputDir)
+     compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
+-    compileClasspath += fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
+-    compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
+     runtimeClasspath = compileClasspath
++    runtimeClasspath += files(sourceSets.main.resources.srcDirs)
+   }
+   test {
+@@ -557,453 +175,41 @@
+     runtimeClasspath = compileClasspath
+     runtimeClasspath += files(sourceSets.test.resources.srcDirs)
+   }
+-
+-}
+-
+-
+-// eclipse project and settings files creation, also used by buildship
+-eclipse {
+-  project {
+-    name = eclipse_project_name
+-
+-    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
+-    configurations.each{ c->
+-      if (c.isCanBeResolved()) {
+-        minusConfigurations += [c]
+-      }
+-    }
+-
+-    plusConfigurations = [ ]
+-    file {
+-
+-      whenMerged { cp ->
+-        def removeTheseToo = []
+-        HashMap<String, Boolean> alreadyAddedSrcPath = new HashMap<>();
+-        cp.entries.each { entry ->
+-          // This conditional removes all src classpathentries that a) have already been added or b) aren't "src" or "test".
+-          // e.g. this removes the resources dir being copied into bin/main, bin/test AND bin/clover
+-          // we add the resources and help/help dirs in as libs afterwards (see below)
+-          if (entry.kind == 'src') {
+-            if (alreadyAddedSrcPath.getAt(entry.path) || !(entry.path == bareSourceDir || entry.path == bareTestSourceDir)) {
+-              removeTheseToo += entry
+-            } else {
+-              alreadyAddedSrcPath.putAt(entry.path, true)
+-            }
+-          }
+-
+-        }
+-        cp.entries.removeAll(removeTheseToo)
+-
+-        //cp.entries += new Output("${eclipse_bin_dir}/main")
+-        if (file(helpParentDir).isDirectory()) {
+-          cp.entries += new Library(fileReference(helpParentDir))
+-        }
+-        if (file(resourceDir).isDirectory()) {
+-          cp.entries += new Library(fileReference(resourceDir))
+-        }
+-
+-        HashMap<String, Boolean> alreadyAddedLibPath = new HashMap<>();
+-
+-        sourceSets.main.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
+-          //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, especially if they don't exist
+-            return false // groovy "continue" in .any closure
+-          }
+-          def itPath = it.toString()
+-          if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
+-            // make relative path
+-            itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
+-          }
+-          if (alreadyAddedLibPath.get(itPath)) {
+-            //println("Not adding duplicate entry "+itPath)
+-          } else {
+-            //println("Adding entry "+itPath)
+-            cp.entries += new Library(fileReference(itPath))
+-            alreadyAddedLibPath.put(itPath, true)
+-          }
+-        }
+-
+-        sourceSets.test.compileClasspath.findAll { it.name.endsWith(".jar") }.any {
+-          //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 "continue" in .any closure
+-          }
+-
+-          def itPath = it.toString()
+-          if (itPath.startsWith("${jalviewDirAbsolutePath}/")) {
+-            itPath = itPath.substring(jalviewDirAbsolutePath.length()+1)
+-          }
+-          if (alreadyAddedLibPath.get(itPath)) {
+-            // don't duplicate
+-          } else {
+-            def lib = new Library(fileReference(itPath))
+-            lib.entryAttributes["test"] = "true"
+-            cp.entries += lib
+-            alreadyAddedLibPath.put(itPath, true)
+-          }
+-        }
+-
+-      } // whenMerged
+-
+-    } // 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 = eclipseJavaRuntimeName
+-
+-    // add in jalview project specific properties/preferences into eclipse core preferences
+-    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)
+-          }
+-        }
+-        // codestyle file -- overrides previous formatter prefs
+-        def csFile = file("${jalviewDirAbsolutePath}/${eclipse_codestyle_file}")
+-        if (csFile.exists()) {
+-          XmlParser parser = new XmlParser()
+-          def profiles = parser.parse(csFile)
+-          def profile = profiles.'profile'.find { p -> (p.'@kind' == "CodeFormatterProfile" && p.'@name' == "Jalview") }
+-          if (profile != null) {
+-            profile.'setting'.each { s ->
+-              def id = s.'@id'
+-              def value = s.'@value'
+-              if (id != null && value != null) {
+-                props.putAt(id, value)
+-              }
+-            }
+-          }
+-        }
+-      }
+-    }
+-
+-  } // jdt
+-
+-  if (IN_ECLIPSE) {
+-    // Don't want these to be activated if in headless build
+-    synchronizationTasks "eclipseSynchronizationTask"
+-    //autoBuildTasks "eclipseAutoBuildTask"
+-
+-  }
+-}
+-
+-
+-/* hack to change eclipse prefs in .settings files other than org.eclipse.jdt.core.prefs */
+-// Class to allow updating arbitrary properties files
+-class PropertiesFile extends PropertiesPersistableConfigurationObject {
+-  public PropertiesFile(PropertiesTransformer t) { super(t); }
+-  @Override protected void load(Properties properties) { }
+-  @Override protected void store(Properties properties) { }
+-  @Override protected String getDefaultResourceName() { return ""; }
+-  // This is necessary, because PropertiesPersistableConfigurationObject fails
+-  // if no default properties file exists.
+-  @Override public void loadDefaults() { load(new StringBufferInputStream("")); }
+-}
+-
+-// Task to update arbitrary properties files (set outputFile)
+-class PropertiesFileTask extends PropertiesGeneratorTask<PropertiesFile> {
+-  private final PropertiesFileContentMerger file;
+-  public PropertiesFileTask() { file = new PropertiesFileContentMerger(getTransformer()); }
+-  protected PropertiesFile create() { return new PropertiesFile(getTransformer()); }
+-  protected void configure(PropertiesFile props) {
+-    file.getBeforeMerged().execute(props); file.getWhenMerged().execute(props);
+-  }
+-  public void file(Closure closure) { ConfigureUtil.configure(closure, file); }
+-}
+-
+-task eclipseUIPreferences(type: PropertiesFileTask) {
+-  description = "Generate Eclipse additional settings"
+-  def filename = "org.eclipse.jdt.ui.prefs"
+-  outputFile = "$projectDir/.settings/${filename}" as File
+-  file {
+-    withProperties {
+-      it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
++ /*  test {
++    java {
++      srcDirs testSourceDir
++      outputDir = file(testClassesDir)
+     }
+-  }
+-}
+-task eclipseGroovyCorePreferences(type: PropertiesFileTask) {
+-  description = "Generate Eclipse additional settings"
+-  def filename = "org.eclipse.jdt.groovy.core.prefs"
+-  outputFile = "$projectDir/.settings/${filename}" as File
+-  file {
+-    withProperties {
+-      it.load new FileInputStream("$projectDir/utils/eclipse/${filename}" as String)
++    resources {
++      srcDirs = sourceSets.main.resources.srcDirs
+     }
+-  }
+-}
+-
+-task eclipseAllPreferences {
+-  dependsOn eclipseJdt
+-  dependsOn eclipseUIPreferences
+-  dependsOn eclipseGroovyCorePreferences
+-}
+-
+-eclipseUIPreferences.mustRunAfter eclipseJdt
+-eclipseGroovyCorePreferences.mustRunAfter eclipseJdt
+-
+-/* end of eclipse preferences hack */
+-
+-
+-// clover bits
+-
+-
+-task cleanClover {
+-  doFirst {
+-    delete cloverBuildDir
+-    delete cloverReportDir
+-  }
+-}
+-
+-
+-task cloverInstrJava(type: JavaExec) {
+-  group = "Verification"
+-  description = "Create clover instrumented source java files"
+-
+-  dependsOn cleanClover
+-
+-  inputs.files(sourceSets.main.allJava)
+-  outputs.dir(cloverInstrDir)
+-
+-  //classpath = fileTree(dir: "${jalviewDir}/${clover_lib_dir}", include: ["*.jar"])
+-  classpath = sourceSets.clover.compileClasspath
+-  main = "com.atlassian.clover.CloverInstr"
+-
+-  def argsList = [
+-    "--encoding",
+-    "UTF-8",
+-    "--initstring",
+-    cloverDb,
+-    "--destdir",
+-    cloverInstrDir.getPath(),
+-  ]
+-  def srcFiles = sourceSets.main.allJava.files
+-  argsList.addAll(
+-    srcFiles.collect(
+-      { file -> file.absolutePath }
+-    )
+-  )
+-  args argsList.toArray()
+-
+-  doFirst {
+-    delete cloverInstrDir
+-    println("Clover: About to instrument "+srcFiles.size() +" files")
+-  }
+-}
+-
+-
+-task cloverInstrTests(type: JavaExec) {
+-  group = "Verification"
+-  description = "Create clover instrumented source test files"
+-
+-  dependsOn cleanClover
+-
+-  inputs.files(testDir)
+-  outputs.dir(cloverTestInstrDir)
+-
+-  classpath = sourceSets.clover.compileClasspath
+-  main = "com.atlassian.clover.CloverInstr"
+-
+-  def argsList = [
+-    "--encoding",
+-    "UTF-8",
+-    "--initstring",
+-    cloverDb,
+-    "--srcdir",
+-    testDir,
+-    "--destdir",
+-    cloverTestInstrDir.getPath(),
+-  ]
+-  args argsList.toArray()
+-
+-  doFirst {
+-    delete cloverTestInstrDir
+-    println("Clover: About to instrument test files")
+-  }
+-}
+-
+-
+-task cloverInstr {
+-  group = "Verification"
+-  description = "Create clover instrumented all source files"
+-
+-  dependsOn cloverInstrJava
+-  dependsOn cloverInstrTests
+-}
+-
+-
+-cloverClasses.dependsOn cloverInstr
+-
+-
+-task cloverConsoleReport(type: JavaExec) {
+-  group = "Verification"
+-  description = "Creates clover console report"
+-
+-  onlyIf {
+-    file(cloverDb).exists()
+-  }
+-
+-  inputs.dir cloverClassesDir
+-
+-  classpath = sourceSets.clover.runtimeClasspath
+-  main = "com.atlassian.clover.reporters.console.ConsoleReporter"
+-
+-  if (cloverreport_mem.length() > 0) {
+-    maxHeapSize = cloverreport_mem
+-  }
+-  if (cloverreport_jvmargs.length() > 0) {
+-    jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
+-  }
+-
+-  def argsList = [
+-    "--alwaysreport",
+-    "--initstring",
+-    cloverDb,
+-    "--unittests"
+-  ]
+-
+-  args argsList.toArray()
+-}
+-
+-
+-task cloverHtmlReport(type: JavaExec) {
+-  group = "Verification"
+-  description = "Creates clover HTML report"
+-
+-  onlyIf {
+-    file(cloverDb).exists()
+-  }
+-
+-  def cloverHtmlDir = cloverReportDir
+-  inputs.dir cloverClassesDir
+-  outputs.dir cloverHtmlDir
+-
+-  classpath = sourceSets.clover.runtimeClasspath
+-  main = "com.atlassian.clover.reporters.html.HtmlReporter"
+-
+-  if (cloverreport_mem.length() > 0) {
+-    maxHeapSize = cloverreport_mem
+-  }
+-  if (cloverreport_jvmargs.length() > 0) {
+-    jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
+-  }
+-
+-  def argsList = [
+-    "--alwaysreport",
+-    "--initstring",
+-    cloverDb,
+-    "--outputdir",
+-    cloverHtmlDir
+-  ]
+-
+-  if (cloverreport_html_options.length() > 0) {
+-    argsList += cloverreport_html_options.split(" ")
+-  }
+-
+-  args argsList.toArray()
+-}
+-
+-
+-task cloverXmlReport(type: JavaExec) {
+-  group = "Verification"
+-  description = "Creates clover XML report"
+-
+-  onlyIf {
+-    file(cloverDb).exists()
+-  }
+-
+-  def cloverXmlFile = "${cloverReportDir}/clover.xml"
+-  inputs.dir cloverClassesDir
+-  outputs.file cloverXmlFile
+-
+-  classpath = sourceSets.clover.runtimeClasspath
+-  main = "com.atlassian.clover.reporters.xml.XMLReporter"
+-
+-  if (cloverreport_mem.length() > 0) {
+-    maxHeapSize = cloverreport_mem
+-  }
+-  if (cloverreport_jvmargs.length() > 0) {
+-    jvmArgs Arrays.asList(cloverreport_jvmargs.split(" "))
+-  }
+-
+-  def argsList = [
+-    "--alwaysreport",
+-    "--initstring",
+-    cloverDb,
+-    "--outfile",
+-    cloverXmlFile
+-  ]
+-
+-  if (cloverreport_xml_options.length() > 0) {
+-    argsList += cloverreport_xml_options.split(" ")
+-  }
+-
+-  args argsList.toArray()
+-}
++    compileClasspath = files( sourceSets.test.java.outputDir )
++    compileClasspath += sourceSets.main.compileClasspath
++    compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**   REMOVE_THIS_GAP  /*.jar"])
+-task cloverReport {
+-  group = "Verification"
+-  description = "Creates clover reports"
+-
+-  dependsOn cloverXmlReport
+-  dependsOn cloverHtmlReport
+-}
+-
+-
+-compileCloverJava {
+-
+-  doFirst {
+-    sourceCompatibility = compile_source_compatibility
+-    targetCompatibility = compile_target_compatibility
+-    options.compilerArgs += additional_compiler_args
+-    print ("Setting target compatibility to "+targetCompatibility+"\n")
++    runtimeClasspath = compileClasspath
+   }
+-  //classpath += configurations.cloverRuntime
++*/
+ }
+-// end clover bits
+ compileJava {
+-  // JBP->BS should the print statement in doFirst refer to compile_target_compatibility ?
+   sourceCompatibility = compile_source_compatibility
+   targetCompatibility = compile_target_compatibility
+   options.compilerArgs = additional_compiler_args
+-  options.encoding = "UTF-8"
+   doFirst {
+     print ("Setting target compatibility to "+compile_target_compatibility+"\n")
+   }
+-
+ }
+ compileTestJava {
+-  sourceCompatibility = compile_source_compatibility
+-  targetCompatibility = compile_target_compatibility
+-  options.compilerArgs = additional_compiler_args
+   doFirst {
++    sourceCompatibility = compile_source_compatibility
++    targetCompatibility = compile_target_compatibility
++    options.compilerArgs = additional_compiler_args
+     print ("Setting target compatibility to "+targetCompatibility+"\n")
+   }
+ }
+@@ -1017,7 +223,6 @@
+ cleanTest {
+-  dependsOn cleanClover
+   doFirst {
+     delete sourceSets.test.java.outputDir
+   }
+@@ -1031,85 +236,6 @@
+ }
+-def convertMdToHtml (FileTree mdFiles, File cssFile) {
+-  MutableDataSet options = new MutableDataSet()
+-
+-  def extensions = new ArrayList<>()
+-  extensions.add(AnchorLinkExtension.create()) 
+-  extensions.add(AutolinkExtension.create())
+-  extensions.add(StrikethroughExtension.create())
+-  extensions.add(TaskListExtension.create())
+-  extensions.add(TablesExtension.create())
+-  extensions.add(TocExtension.create())
+-  
+-  options.set(Parser.EXTENSIONS, extensions)
+-
+-  // set GFM table parsing options
+-  options.set(TablesExtension.WITH_CAPTION, false)
+-  options.set(TablesExtension.COLUMN_SPANS, false)
+-  options.set(TablesExtension.MIN_HEADER_ROWS, 1)
+-  options.set(TablesExtension.MAX_HEADER_ROWS, 1)
+-  options.set(TablesExtension.APPEND_MISSING_COLUMNS, true)
+-  options.set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
+-  options.set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
+-  // GFM anchor links
+-  options.set(AnchorLinkExtension.ANCHORLINKS_SET_ID, false)
+-  options.set(AnchorLinkExtension.ANCHORLINKS_ANCHOR_CLASS, "anchor")
+-  options.set(AnchorLinkExtension.ANCHORLINKS_SET_NAME, true)
+-  options.set(AnchorLinkExtension.ANCHORLINKS_TEXT_PREFIX, "<span class=\"octicon octicon-link\"></span>")
+-
+-  Parser parser = Parser.builder(options).build()
+-  HtmlRenderer renderer = HtmlRenderer.builder(options).build()
+-
+-  mdFiles.each { mdFile ->
+-    // add table of contents
+-    def mdText = "[TOC]\n"+mdFile.text
+-
+-    // grab the first top-level title
+-    def title = null
+-    def titleRegex = /(?m)^#(\s+|([^#]))(.*)/
+-    def matcher = mdText =~ titleRegex
+-    if (matcher.size() > 0) {
+-      // matcher[0][2] is the first character of the title if there wasn't any whitespace after the #
+-      title = (matcher[0][2] != null ? matcher[0][2] : "")+matcher[0][3]
+-    }
+-    // or use the filename if none found
+-    if (title == null) {
+-      title = mdFile.getName()
+-    }
+-
+-    Node document = parser.parse(mdText)
+-    String htmlBody = renderer.render(document)
+-    def htmlText = '''<html>
+-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+-<html xmlns="http://www.w3.org/1999/xhtml">
+-  <head>
+-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+-    <meta http-equiv="Content-Style-Type" content="text/css" />
+-    <meta name="generator" content="flexmark" />
+-'''
+-    htmlText += ((title != null) ? "  <title>${title}</title>" : '' )
+-    htmlText += '''
+-    <style type="text/css">code{white-space: pre;}</style>
+-'''
+-    htmlText += ((cssFile != null) ? cssFile.text : '')
+-    htmlText += '''</head>
+-  <body>
+-'''
+-    htmlText += htmlBody
+-    htmlText += '''
+-  </body>
+-</html>
+-'''
+-
+-    def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
+-    def htmlFile = file(htmlFilePath)
+-    println("Creating ${htmlFilePath}")
+-    htmlFile.text = htmlText
+-  }
+-}
+-
+-
+ task copyDocs(type: Copy) {
+   def inputDir = "${jalviewDir}/${doc_dir}"
+   def outputDir = "${docBuildDir}/${doc_dir}"
+@@ -1140,27 +266,6 @@
+ }
+-task convertMdFiles {
+-  dependsOn copyDocs
+-  def mdFiles = fileTree(dir: docBuildDir, include: "**/*.md")
+-  def cssFile = file("${jalviewDir}/${flexmark_css}")
+-
+-  doLast {
+-    convertMdToHtml(mdFiles, cssFile)
+-  }
+-
+-  inputs.files(mdFiles)
+-  inputs.file(cssFile)
+-
+-  def htmlFiles = []
+-  mdFiles.each { mdFile ->
+-    def htmlFilePath = mdFile.getPath().replaceAll(/\..*?$/, ".html")
+-    htmlFiles.add(file(htmlFilePath))
+-  }
+-  outputs.files(htmlFiles)
+-}
+-
+-
+ task copyHelp(type: Copy) {
+   def inputDir = helpSourceDir
+   def outputDir = "${helpBuildDir}/${help_dir}"
+@@ -1242,24 +347,15 @@
+   outputs.dir(outputDir)
+ }
+-task createBuildProperties(type: WriteProperties) {
+-  dependsOn copyResources
+-  group = "build"
+-  description = "Create the ${buildProperties} file"
+-  
+-  inputs.dir(sourceDir)
+-  inputs.dir(resourcesBuildDir)
+-  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:"+gitHash+" ["+gitBranch+"]"
+-  if (getdownSetAppBaseProperty) {
+-    property "GETDOWNAPPBASE", getdownAppBase
+-    property "GETDOWNAPPDISTDIR", getdownAppDistDir
+-  }
++task createBuildProperties(type: Copy) {
++  // using the build_properties already included in the source tarball
++  def inputFile = "build_properties"
++  def outputFile = buildProperties
++  from inputFile
++  into file(outputFile).getParent()
++  rename(file(inputFile).getName(), file(outputFile).getName())
++
++  inputs.file(inputFile)
+   outputs.file(outputFile)
+ }
+@@ -1293,7 +389,6 @@
+   dependsOn buildResources
+   dependsOn copyDocs
+   dependsOn copyHelp
+-  dependsOn convertMdFiles
+   dependsOn buildIndices
+ }
+@@ -1306,12 +401,7 @@
+ //testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
+ test {
+   dependsOn prepare
+-
+-  if (useClover) {
+-    dependsOn cloverClasses
+-   } else { //?
+-    dependsOn compileJava //?
+-  }
++  dependsOn compileJava //?
+   useTestNG() {
+     includeGroups testng_groups
+@@ -1323,6 +413,7 @@
+   maxHeapSize = "1024m"
+   workingDir = jalviewDir
++  //systemProperties 'clover.jar' System.properties.clover.jar
+   def testLaf = project.findProperty("test_laf")
+   if (testLaf != null) {
+     println("Setting Test LaF to '${testLaf}'")
+@@ -1338,9 +429,6 @@
+   jvmArgs += additional_compiler_args
+   doFirst {
+-    if (useClover) {
+-      println("Running tests " + (useClover?"WITH":"WITHOUT") + " clover")
+-    }
+   }
+ }
+@@ -1420,1752 +508,7 @@
+   sourceSets.main.resources.srcDirs.each{ dir ->
+     inputs.dir(dir)
+   }
+-  outputs.file("${outputDir}/${archiveFileName}")
+-}
+-
+-
+-task copyJars(type: Copy) {
+-  from fileTree(dir: classesDir, include: "**/*.jar").files
+-  into "${jalviewDir}/${package_dir}"
+-}
+-
+-
+-// doing a Sync instead of Copy as Copy doesn't deal with "outputs" very well
+-task syncJars(type: Sync) {
+-  dependsOn jar
+-  from fileTree(dir: "${jalviewDir}/${libDistDir}", include: "**/*.jar").files
+-  into "${jalviewDir}/${package_dir}"
+-  preserve {
+-    include jar.archiveFileName.getOrNull()
+-  }
+-}
+-
+-
+-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}/${package_dir}")
+-}
+-
+-
+-task cleanDist {
+-  dependsOn cleanPackageDir
+-  dependsOn cleanTest
+-  dependsOn clean
+-}
+-
+-
+-shadowJar {
+-  group = "distribution"
+-  description = "Create a single jar file with all dependency libraries merged. Can be run with java -jar"
+-  if (buildDist) {
+-    dependsOn makeDist
+-  }
+-  from ("${jalviewDir}/${libDistDir}") {
+-    include("*.jar")
+-  }
+-  manifest {
+-    attributes "Implementation-Version": JALVIEW_VERSION,
+-    "Application-Name": install4jApplicationName
+-  }
+-  mainClassName = shadow_jar_main_class
+-  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
+-  }
+-
+-  def getdownWebsiteResourceFilenames = []
+-  def getdownTextString = ""
+-  def getdownResourceDir = getdownResourceDir
+-  def getdownResourceFilenames = []
+-
+-  doFirst {
+-    // clean the getdown website and files dir before creating getdown folders
+-    delete getdownWebsiteDir
+-    delete getdownFilesDir
+-
+-    copy {
+-      from buildProperties
+-      rename(file(buildProperties).getName(), getdown_build_properties)
+-      into getdownAppDir
+-    }
+-    getdownWebsiteResourceFilenames += "${getdownAppDistDir}/${getdown_build_properties}"
+-
+-    // set some getdown_txt_ properties then go through all properties looking for getdown_txt_...
+-    def props = project.properties.sort { it.key }
+-    if (getdownAltJavaMinVersion != null && getdownAltJavaMinVersion.length() > 0) {
+-      props.put("getdown_txt_java_min_version", getdownAltJavaMinVersion)
+-    }
+-    if (getdownAltJavaMaxVersion != null && getdownAltJavaMaxVersion.length() > 0) {
+-      props.put("getdown_txt_java_max_version", getdownAltJavaMaxVersion)
+-    }
+-    if (getdownAltMultiJavaLocation != null && getdownAltMultiJavaLocation.length() > 0) {
+-      props.put("getdown_txt_multi_java_location", getdownAltMultiJavaLocation)
+-    }
+-    if (getdownImagesDir != null && file(getdownImagesDir).exists()) {
+-      props.put("getdown_txt_ui.background_image", "${getdownImagesDir}/${getdown_background_image}")
+-      props.put("getdown_txt_ui.instant_background_image", "${getdownImagesDir}/${getdown_instant_background_image}")
+-      props.put("getdown_txt_ui.error_background", "${getdownImagesDir}/${getdown_error_background}")
+-      props.put("getdown_txt_ui.progress_image", "${getdownImagesDir}/${getdown_progress_image}")
+-      props.put("getdown_txt_ui.icon", "${getdownImagesDir}/${getdown_icon}")
+-      props.put("getdown_txt_ui.mac_dock_icon", "${getdownImagesDir}/${getdown_mac_dock_icon}")
+-    }
+-
+-    props.put("getdown_txt_title", jalview_name)
+-    props.put("getdown_txt_ui.name", install4jApplicationName)
+-
+-    // start with appbase
+-    getdownTextString += "appbase = ${getdownAppBase}\n"
+-    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 getdownResourceDir
+-      }
+-    }
+-    
+-    def getdownWrapperScripts = [ getdown_bash_wrapper_script, getdown_powershell_wrapper_script, getdown_batch_wrapper_script ]
+-    getdownWrapperScripts.each{ script ->
+-      def s = file( "${jalviewDir}/utils/getdown/${getdown_wrapper_script_dir}/${script}" )
+-      if (s.exists()) {
+-        copy {
+-          from s
+-          into "${getdownWebsiteDir}/${getdown_wrapper_script_dir}"
+-        }
+-        getdownTextString += "resource = ${getdown_wrapper_script_dir}/${script}\n"
+-      }
+-    }
+-
+-    def codeFiles = []
+-    fileTree(file(package_dir)).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 name = f.getName()
+-      def line = "code = ${getdownAppDistDir}/${name}\n"
+-      getdownTextString += line
+-      copy {
+-        from f.getPath()
+-        into 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 name = f.getName()
+-    def line = "code = ${getdown_j11lib_dir}/${name}\n"
+-    getdownTextString += line
+-    copy {
+-    from f.getPath()
+-    into 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 = ${main_class}\n"
+-    // Not setting these properties in general so that getdownappbase and getdowndistdir will default to release version in jalview.bin.Cache
+-    if (getdownSetAppBaseProperty) {
+-      getdownTextString += "jvmarg = -Dgetdowndistdir=${getdownAppDistDir}\n"
+-      getdownTextString += "jvmarg = -Dgetdownappbase=${getdownAppBase}\n"
+-    }
+-
+-    def getdown_txt = file("${getdownWebsiteDir}/getdown.txt")
+-    getdown_txt.write(getdownTextString)
+-
+-    def getdownLaunchJvl = getdown_launch_jvl_name + ( (jvlChannelName != null && jvlChannelName.length() > 0)?"-${jvlChannelName}":"" ) + ".jvl"
+-    def launchJvl = file("${getdownWebsiteDir}/${getdownLaunchJvl}")
+-    launchJvl.write("appbase=${getdownAppBase}")
+-
+-    // files going into the getdown website dir: getdown-launcher.jar
+-    copy {
+-      from getdownLauncher
+-      rename(file(getdownLauncher).getName(), getdown_launcher_new)
+-      into getdownWebsiteDir
+-    }
+-
+-    // files going into the getdown website dir: getdown-launcher(-local).jar
+-    copy {
+-      from getdownLauncher
+-      if (file(getdownLauncher).getName() != getdown_launcher) {
+-        rename(file(getdownLauncher).getName(), getdown_launcher)
+-      }
+-      into getdownWebsiteDir
+-    }
+-
+-    // files going into the getdown website dir: ./install dir and files
+-    if (! (CHANNEL.startsWith("ARCHIVE") || CHANNEL.startsWith("DEVELOP"))) {
+-      copy {
+-        from getdown_txt
+-        from getdownLauncher
+-        from "${getdownAppDir}/${getdown_build_properties}"
+-        if (file(getdownLauncher).getName() != getdown_launcher) {
+-          rename(file(getdownLauncher).getName(), getdown_launcher)
+-        }
+-        into getdownInstallDir
+-      }
+-
+-      // and make a copy in the getdown files dir (these are not downloaded by getdown)
+-      copy {
+-        from getdownInstallDir
+-        into getdownFilesInstallDir
+-      }
+-    }
+-
+-    // files going into the getdown files dir: getdown.txt, getdown-launcher.jar, channel-launch.jvl, build_properties
+-    copy {
+-      from getdown_txt
+-      from launchJvl
+-      from getdownLauncher
+-      from "${getdownWebsiteDir}/${getdown_build_properties}"
+-      if (file(getdownLauncher).getName() != getdown_launcher) {
+-        rename(file(getdownLauncher).getName(), getdown_launcher)
+-      }
+-      into getdownFilesDir
+-    }
+-
+-    // and ./resources (not all downloaded by getdown)
+-    copy {
+-      from getdownResourceDir
+-      into "${getdownFilesDir}/${getdown_resource_dir}"
+-    }
+-  }
+-
+-  if (buildDist) {
+-    inputs.dir("${jalviewDir}/${package_dir}")
+-  }
+-  outputs.dir(getdownWebsiteDir)
+-  outputs.dir(getdownFilesDir)
+-}
+-
+-
+-// a helper task to allow getdown digest of any dir: `gradle getdownDigestDir -PDIGESTDIR=/path/to/my/random/getdown/dir
+-task getdownDigestDir(type: JavaExec) {
+-  group "Help"
+-  description "A task to run a getdown Digest on a dir with getdown.txt. Provide a DIGESTDIR property via -PDIGESTDIR=..."
+-
+-  def digestDirPropertyName = "DIGESTDIR"
+-  doFirst {
+-    classpath = files(getdownLauncher)
+-    def digestDir = findProperty(digestDirPropertyName)
+-    if (digestDir == null) {
+-      throw new GradleException("Must provide a DIGESTDIR value to produce an alternative getdown digest")
+-    }
+-    args digestDir
+-  }
+-  main = "com.threerings.getdown.tools.Digester"
+-}
+-
+-
+-task getdownDigest(type: JavaExec) {
+-  group = "distribution"
+-  description = "Digest the getdown website folder"
+-  dependsOn getdownWebsite
+-  doFirst {
+-    classpath = files(getdownLauncher)
+-  }
+-  main = "com.threerings.getdown.tools.Digester"
+-  args getdownWebsiteDir
+-  inputs.dir(getdownWebsiteDir)
+-  outputs.file("${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
+-        }
+-      }
+-    }
+-  }
+-}
+-
+-
+-tasks.withType(JavaCompile) {
+-      options.encoding = 'UTF-8'
+-}
+-
+-
+-clean {
+-  doFirst {
+-    delete getdownWebsiteDir
+-    delete getdownFilesDir
+-  }
+-}
+-
+-
+-install4j {
+-  if (file(install4jHomeDir).exists()) {
+-    // good to go!
+-  } else if (file(System.getProperty("user.home")+"/buildtools/install4j").exists()) {
+-    install4jHomeDir = System.getProperty("user.home")+"/buildtools/install4j"
+-  } else if (file("/Applications/install4j.app/Contents/Resources/app").exists()) {
+-    install4jHomeDir = "/Applications/install4j.app/Contents/Resources/app"
+-  }
+-  installDir(file(install4jHomeDir))
+-
+-  mediaTypes = Arrays.asList(install4j_media_types.split(","))
+-}
+-
+-
+-task copyInstall4jTemplate {
+-  def install4jTemplateFile = file("${install4jDir}/${install4j_template}")
+-  def install4jFileAssociationsFile = file("${install4jDir}/${install4j_installer_file_associations}")
+-  inputs.file(install4jTemplateFile)
+-  inputs.file(install4jFileAssociationsFile)
+-  inputs.property("CHANNEL", { CHANNEL })
+-  outputs.file(install4jConfFile)
+-
+-  doLast {
+-    def install4jConfigXml = new XmlParser().parse(install4jTemplateFile)
+-
+-    // turn off code signing if no OSX_KEYPASS
+-    if (OSX_KEYPASS == "") {
+-      install4jConfigXml.'**'.codeSigning.each { codeSigning ->
+-        codeSigning.'@macEnabled' = "false"
+-      }
+-      install4jConfigXml.'**'.windows.each { windows ->
+-        windows.'@runPostProcessor' = "false"
+-      }
+-    }
+-
+-    // turn off checksum creation for LOCAL channel
+-    def e = install4jConfigXml.application[0]
+-    if (CHANNEL == "LOCAL") {
+-      e.'@createChecksums' = "false"
+-    } else {
+-      e.'@createChecksums' = "true"
+-    }
+-
+-    // put file association actions where placeholder action is
+-    def install4jFileAssociationsText = install4jFileAssociationsFile.text
+-    def fileAssociationActions = new XmlParser().parseText("<actions>${install4jFileAssociationsText}</actions>")
+-    install4jConfigXml.'**'.action.any { a -> // .any{} stops after the first one that returns true
+-      if (a.'@name' == 'EXTENSIONS_REPLACED_BY_GRADLE') {
+-        def parent = a.parent()
+-        parent.remove(a)
+-        fileAssociationActions.each { faa ->
+-            parent.append(faa)
+-        }
+-        // don't need to continue in .any loop once replacements have been made
+-        return true
+-      }
+-    }
+-
+-    // use Windows Program Group with Examples folder for RELEASE, and Program Group without Examples for everything else
+-    // NB we're deleting the /other/ one!
+-    // Also remove the examples subdir from non-release versions
+-    def customizedIdToDelete = "PROGRAM_GROUP_RELEASE"
+-    // 2.11.1.0 NOT releasing with the Examples folder in the Program Group
+-    if (false && CHANNEL=="RELEASE") { // remove 'false && ' to include Examples folder in RELEASE channel
+-      customizedIdToDelete = "PROGRAM_GROUP_NON_RELEASE"
+-    } else {
+-      // remove the examples subdir from Full File Set
+-      def files = install4jConfigXml.files[0]
+-      def fileset = files.filesets.fileset.find { fs -> fs.'@customizedId' == "FULL_FILE_SET" }
+-      def root = files.roots.root.find { r -> r.'@fileset' == fileset.'@id' }
+-      def mountPoint = files.mountPoints.mountPoint.find { mp -> mp.'@root' == root.'@id' }
+-      def dirEntry = files.entries.dirEntry.find { de -> de.'@mountPoint' == mountPoint.'@id' && de.'@subDirectory' == "examples" }
+-      dirEntry.parent().remove(dirEntry)
+-    }
+-    install4jConfigXml.'**'.action.any { a ->
+-      if (a.'@customizedId' == customizedIdToDelete) {
+-        def parent = a.parent()
+-        parent.remove(a)
+-        return true
+-      }
+-    }
+-
+-    // write install4j file
+-    install4jConfFile.text = XmlUtil.serialize(install4jConfigXml)
+-  }
+-}
+-
+-
+-clean {
+-  doFirst {
+-    delete install4jConfFile
+-  }
+-}
+-
+-
+-task installers(type: com.install4j.gradle.Install4jTask) {
+-  group = "distribution"
+-  description = "Create the install4j installers"
+-  dependsOn getdown
+-  dependsOn copyInstall4jTemplate
+-
+-  projectFile = install4jConfFile
+-
+-  // create an md5 for the input files to use as version for install4j conf file
+-  def digest = MessageDigest.getInstance("MD5")
+-  digest.update(
+-    (file("${install4jDir}/${install4j_template}").text + 
+-    file("${install4jDir}/${install4j_info_plist_file_associations}").text +
+-    file("${install4jDir}/${install4j_installer_file_associations}").text).bytes)
+-  def filesMd5 = new BigInteger(1, digest.digest()).toString(16)
+-  if (filesMd5.length() >= 8) {
+-    filesMd5 = filesMd5.substring(0,8)
+-  }
+-  def install4jTemplateVersion = "${JALVIEW_VERSION}_F${filesMd5}_C${gitHash}"
+-  // make install4jBuildDir relative to jalviewDir
+-  def install4jBuildDir = "${install4j_build_dir}/${JAVA_VERSION}"
+-
+-  variables = [
+-    'JALVIEW_NAME': jalview_name,
+-    'JALVIEW_APPLICATION_NAME': install4jApplicationName,
+-    'JALVIEW_DIR': "../..",
+-    'OSX_KEYSTORE': OSX_KEYSTORE,
+-    'OSX_APPLEID': OSX_APPLEID,
+-    'OSX_ALTOOLPASS': OSX_ALTOOLPASS,
+-    'JSIGN_SH': JSIGN_SH,
+-    'JRE_DIR': getdown_app_dir_java,
+-    'INSTALLER_TEMPLATE_VERSION': install4jTemplateVersion,
+-    'JALVIEW_VERSION': JALVIEW_VERSION,
+-    'JAVA_MIN_VERSION': JAVA_MIN_VERSION,
+-    'JAVA_MAX_VERSION': JAVA_MAX_VERSION,
+-    'JAVA_VERSION': JAVA_VERSION,
+-    'JAVA_INTEGER_VERSION': JAVA_INTEGER_VERSION,
+-    'VERSION': JALVIEW_VERSION,
+-    'MACOS_JAVA_VM_DIR': macosJavaVMDir,
+-    'WINDOWS_JAVA_VM_DIR': windowsJavaVMDir,
+-    'LINUX_JAVA_VM_DIR': linuxJavaVMDir,
+-    'MACOS_JAVA_VM_TGZ': macosJavaVMTgz,
+-    'WINDOWS_JAVA_VM_TGZ': windowsJavaVMTgz,
+-    'LINUX_JAVA_VM_TGZ': linuxJavaVMTgz,
+-    'COPYRIGHT_MESSAGE': install4j_copyright_message,
+-    'BUNDLE_ID': install4jBundleId,
+-    'INTERNAL_ID': install4jInternalId,
+-    'WINDOWS_APPLICATION_ID': install4jWinApplicationId,
+-    'MACOS_DMG_DS_STORE': install4jDMGDSStore,
+-    'MACOS_DMG_BG_IMAGE': install4jDMGBackgroundImage,
+-    'WRAPPER_LINK': getdownWrapperLink,
+-    'BASH_WRAPPER_SCRIPT': getdown_bash_wrapper_script,
+-    'POWERSHELL_WRAPPER_SCRIPT': getdown_powershell_wrapper_script,
+-    'WRAPPER_SCRIPT_BIN_DIR': getdown_wrapper_script_dir,
+-    'INSTALLER_NAME': install4jInstallerName,
+-    'INSTALL4J_UTILS_DIR': install4j_utils_dir,
+-    'GETDOWN_WEBSITE_DIR': getdown_website_dir,
+-    'GETDOWN_FILES_DIR': getdown_files_dir,
+-    'GETDOWN_RESOURCE_DIR': getdown_resource_dir,
+-    'GETDOWN_DIST_DIR': getdownAppDistDir,
+-    'GETDOWN_ALT_DIR': getdown_app_dir_alt,
+-    'GETDOWN_INSTALL_DIR': getdown_install_dir,
+-    'INFO_PLIST_FILE_ASSOCIATIONS_FILE': install4j_info_plist_file_associations,
+-    'BUILD_DIR': install4jBuildDir,
+-    'APPLICATION_CATEGORIES': install4j_application_categories,
+-    'APPLICATION_FOLDER': install4jApplicationFolder,
+-    'UNIX_APPLICATION_FOLDER': install4jUnixApplicationFolder,
+-    'EXECUTABLE_NAME': install4jExecutableName,
+-    'EXTRA_SCHEME': install4jExtraScheme,
+-    'MAC_ICONS_FILE': install4jMacIconsFile,
+-    'WINDOWS_ICONS_FILE': install4jWindowsIconsFile,
+-    'PNG_ICON_FILE': install4jPngIconFile,
+-    'BACKGROUND': install4jBackground,
+-
+-  ]
+-
+-  //println("INSTALL4J VARIABLES:")
+-  //variables.each{k,v->println("${k}=${v}")}
+-
+-  destination = "${jalviewDir}/${install4jBuildDir}"
+-  buildSelected = true
+-
+-  if (install4j_faster.equals("true") || CHANNEL.startsWith("LOCAL")) {
+-    faster = true
+-    disableSigning = true
+-    disableNotarization = true
+-  }
+-
+-  if (OSX_KEYPASS) {
+-    macKeystorePassword = OSX_KEYPASS
+-  } 
+-  
+-  if (OSX_ALTOOLPASS) {
+-    appleIdPassword = OSX_ALTOOLPASS
+-    disableNotarization = false
+-  } else {
+-    disableNotarization = true
+-  }
+-
+-  doFirst {
+-    println("Using projectFile "+projectFile)
+-    if (!disableNotarization) { println("Will notarize OSX App DMG") }
+-  }
+-  //verbose=true
+-
+-  inputs.dir(getdownWebsiteDir)
+-  inputs.file(install4jConfFile)
+-  inputs.file("${install4jDir}/${install4j_info_plist_file_associations}")
+-  inputs.dir(macosJavaVMDir)
+-  inputs.dir(windowsJavaVMDir)
+-  outputs.dir("${jalviewDir}/${install4j_build_dir}/${JAVA_VERSION}")
+-}
+-
+-
+-spotless {
+-  java {
+-    eclipse().configFile(eclipse_codestyle_file)
+-  }
+-}
+-
+-
+-task sourceDist(type: Tar) {
+-  group "distribution"
+-  description "Create a source .tar.gz file for distribution"
+-
+-  dependsOn createBuildProperties
+-  dependsOn convertMdFiles
+-
+-  def VERSION_UNDERSCORES = JALVIEW_VERSION.replaceAll("\\.", "_")
+-  def outputFileName = "${project.name}_${VERSION_UNDERSCORES}.tar.gz"
+-  archiveFileName = 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",
+-    "**/*.log",
+-  ] 
+-  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",
+-  ]
+-  def INCLUDE_FILES=[
+-    ".settings/org.eclipse.jdt.core.jalview.prefs",
+-  ]
+-
+-  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) {
+-    include(INCLUDE_FILES)
+-  }
+-//  from (jalviewDir) {
+-//    // explicit includes for stuff that seemed to not get included
+-//    include(fileTree("test/**/*."))
+-//    exclude(EXCLUDE_FILES)
+-//    exclude(PROCESS_FILES)
+-//  }
+-
+-  from(file(buildProperties).getParent()) {
+-    include(file(buildProperties).getName())
+-    rename(file(buildProperties).getName(), "build_properties")
+-    filter({ line ->
+-      line.replaceAll("^INSTALLATION=.*\$","INSTALLATION=Source Release"+" git-commit\\\\:"+gitHash+" ["+gitBranch+"]")
+-    })
+-  }
+-
+-}
+-
+-
+-task helppages {
+-  dependsOn copyHelp
+-  dependsOn pubhtmlhelp
+-  
+-  inputs.dir("${helpBuildDir}/${help_dir}")
+-  outputs.dir("${buildDir}/distributions/${help_dir}")
+-}
+-
+-
+-task j2sSetHeadlessBuild {
+-  doFirst {
+-    IN_ECLIPSE = false
+-  }
+-}
+-
+-
+-task jalviewjsEnableAltFileProperty(type: WriteProperties) {
+-  group "jalviewjs"
+-  description "Enable the alternative J2S Config file for headless build"
+-
+-  outputFile = jalviewjsJ2sSettingsFileName
+-  def j2sPropsFile = file(jalviewjsJ2sSettingsFileName)
+-  def j2sProps = new Properties()
+-  if (j2sPropsFile.exists()) {
+-    try {
+-      def j2sPropsFileFIS = new FileInputStream(j2sPropsFile)
+-      j2sProps.load(j2sPropsFileFIS)
+-      j2sPropsFileFIS.close()
+-
+-      j2sProps.each { prop, val ->
+-        property(prop, val)
+-      }
+-    } catch (Exception e) {
+-      println("Exception reading ${jalviewjsJ2sSettingsFileName}")
+-      e.printStackTrace()
+-    }
+-  }
+-  if (! j2sProps.stringPropertyNames().contains(jalviewjs_j2s_alt_file_property_config)) {
+-    property(jalviewjs_j2s_alt_file_property_config, jalviewjs_j2s_alt_file_property)
+-  }
+-}
+-
+-
+-task jalviewjsSetEclipseWorkspace {
+-  def propKey = "jalviewjs_eclipse_workspace"
+-  def propVal = null
+-  if (project.hasProperty(propKey)) {
+-    propVal = project.getProperty(propKey)
+-    if (propVal.startsWith("~/")) {
+-      propVal = System.getProperty("user.home") + propVal.substring(1)
+-    }
+-  }
+-  def propsFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_workspace_location_file}"
+-  def propsFile = file(propsFileName)
+-  def eclipseWsDir = propVal
+-  def props = new Properties()
+-
+-  def writeProps = true
+-  if (( eclipseWsDir == null || !file(eclipseWsDir).exists() ) && propsFile.exists()) {
+-    def ins = new FileInputStream(propsFileName)
+-    props.load(ins)
+-    ins.close()
+-    if (props.getProperty(propKey, null) != null) {
+-      eclipseWsDir = props.getProperty(propKey)
+-      writeProps = false
+-    }
+-  }
+-
+-  if (eclipseWsDir == null || !file(eclipseWsDir).exists()) {
+-    def tempDir = File.createTempDir()
+-    eclipseWsDir = tempDir.getAbsolutePath()
+-    writeProps = true
+-  }
+-  eclipseWorkspace = file(eclipseWsDir)
+-
+-  doFirst {
+-    // do not run a headless transpile when we claim to be in Eclipse
+-    if (IN_ECLIPSE) {
+-      println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+-      throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
+-    } else {
+-      println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+-    }
+-
+-    if (writeProps) {
+-      props.setProperty(propKey, eclipseWsDir)
+-      propsFile.parentFile.mkdirs()
+-      def bytes = new ByteArrayOutputStream()
+-      props.store(bytes, null)
+-      def propertiesString = bytes.toString()
+-      propsFile.text = propertiesString
+-      print("NEW ")
+-    } else {
+-      print("EXISTING ")
+-    }
+-
+-    println("ECLIPSE WORKSPACE: "+eclipseWorkspace.getPath())
+-  }
+-
+-  //inputs.property(propKey, eclipseWsDir) // eclipseWsDir only gets set once this task runs, so will be out-of-date
+-  outputs.file(propsFileName)
+-  outputs.upToDateWhen { eclipseWorkspace.exists() && propsFile.exists() }
+-}
+-
+-
+-task jalviewjsEclipsePaths {
+-  def eclipseProduct
+-
+-  def eclipseRoot = jalviewjs_eclipse_root
+-  if (eclipseRoot.startsWith("~/")) {
+-    eclipseRoot = System.getProperty("user.home") + eclipseRoot.substring(1)
+-  }
+-  if (OperatingSystem.current().isMacOsX()) {
+-    eclipseRoot += "/Eclipse.app"
+-    eclipseBinary = "${eclipseRoot}/Contents/MacOS/eclipse"
+-    eclipseProduct = "${eclipseRoot}/Contents/Eclipse/.eclipseproduct"
+-  } else if (OperatingSystem.current().isWindows()) { // check these paths!!
+-    if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
+-      eclipseRoot += "/eclipse"
+-    }
+-    eclipseBinary = "${eclipseRoot}/eclipse.exe"
+-    eclipseProduct = "${eclipseRoot}/.eclipseproduct"
+-  } else { // linux or unix
+-    if (file("${eclipseRoot}/eclipse").isDirectory() && file("${eclipseRoot}/eclipse/.eclipseproduct").exists()) {
+-      eclipseRoot += "/eclipse"
+-println("eclipseDir exists")
+-    }
+-    eclipseBinary = "${eclipseRoot}/eclipse"
+-    eclipseProduct = "${eclipseRoot}/.eclipseproduct"
+-  }
+-
+-  eclipseVersion = "4.13" // default
+-  def assumedVersion = true
+-  if (file(eclipseProduct).exists()) {
+-    def fis = new FileInputStream(eclipseProduct)
+-    def props = new Properties()
+-    props.load(fis)
+-    eclipseVersion = props.getProperty("version")
+-    fis.close()
+-    assumedVersion = false
+-  }
+-  
+-  def propKey = "eclipse_debug"
+-  eclipseDebug = (project.hasProperty(propKey) && project.getProperty(propKey).equals("true"))
+-
+-  doFirst {
+-    // do not run a headless transpile when we claim to be in Eclipse
+-    if (IN_ECLIPSE) {
+-      println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+-      throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
+-    } else {
+-      println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+-    }
+-
+-    if (!assumedVersion) {
+-      println("ECLIPSE VERSION=${eclipseVersion}")
+-    }
+-  }
+-}
+-
+-
+-task printProperties {
+-  group "Debug"
+-  description "Output to console all System.properties"
+-  doFirst {
+-    System.properties.each { key, val -> System.out.println("Property: ${key}=${val}") }
+-  }
+-}
+-
+-
+-task eclipseSetup {
+-  dependsOn eclipseProject
+-  dependsOn eclipseClasspath
+-  dependsOn eclipseJdt
+-}
+-
+-
+-// this version (type: Copy) will delete anything in the eclipse dropins folder that isn't in fromDropinsDir
+-task jalviewjsEclipseCopyDropins(type: Copy) {
+-  dependsOn jalviewjsEclipsePaths
+-
+-  def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_eclipse_dropins_dir}", include: "*.jar")
+-  inputFiles += file("${jalviewDir}/${jalviewjsJ2sPlugin}")
+-  def outputDir = "${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}"
+-
+-  from inputFiles
+-  into outputDir
+-}
+-
+-
+-// this eclipse -clean doesn't actually work
+-task jalviewjsCleanEclipse(type: Exec) {
+-  dependsOn eclipseSetup
+-  dependsOn jalviewjsEclipsePaths
+-  dependsOn jalviewjsEclipseCopyDropins
+-
+-  executable(eclipseBinary)
+-  args(["-nosplash", "--launcher.suppressErrors", "-data", eclipseWorkspace.getPath(), "-clean", "-console", "-consoleLog"])
+-  if (eclipseDebug) {
+-    args += "-debug"
+-  }
+-  args += "-l"
+-
+-  def inputString = """exit
+-y
+-"""
+-  def inputByteStream = new ByteArrayInputStream(inputString.getBytes())
+-  standardInput = inputByteStream
+-}
+-
+-/* not really working yet
+-jalviewjsEclipseCopyDropins.finalizedBy jalviewjsCleanEclipse
+-*/
+-
+-
+-task jalviewjsTransferUnzipSwingJs {
+-  def file_zip = "${jalviewDir}/${jalviewjs_swingjs_zip}"
+-
+-  doLast {
+-    copy {
+-      from zipTree(file_zip)
+-      into "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
+-    }
+-  }
+-
+-  inputs.file file_zip
+-  outputs.dir "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
+-}
+-
+-
+-task jalviewjsTransferUnzipLib {
+-  def zipFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_libjs_dir}", include: "*.zip")
+-
+-  doLast {
+-    zipFiles.each { file_zip -> 
+-      copy {
+-        from zipTree(file_zip)
+-        into "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
+-      }
+-    }
+-  }
+-
+-  inputs.files zipFiles
+-  outputs.dir "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
+-}
+-
+-
+-task jalviewjsTransferUnzipAllLibs {
+-  dependsOn jalviewjsTransferUnzipSwingJs
+-  dependsOn jalviewjsTransferUnzipLib
+-}
+-
+-
+-task jalviewjsCreateJ2sSettings(type: WriteProperties) {
+-  group "JalviewJS"
+-  description "Create the alternative j2s file from the j2s.* properties"
+-
+-  jalviewjsJ2sProps = project.properties.findAll { it.key.startsWith("j2s.") }.sort { it.key }
+-  def siteDirProperty = "j2s.site.directory"
+-  def setSiteDir = false
+-  jalviewjsJ2sProps.each { prop, val ->
+-    if (val != null) {
+-      if (prop == siteDirProperty) {
+-        if (!(val.startsWith('/') || val.startsWith("file://") )) {
+-          val = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${val}"
+-        }
+-        setSiteDir = true
+-      }
+-      property(prop,val)
+-    }
+-    if (!setSiteDir) { // default site location, don't override specifically set property
+-      property(siteDirProperty,"${jalviewDirRelativePath}/${jalviewjsTransferSiteJsDir}")
+-    }
+-  }
+-  outputFile = jalviewjsJ2sAltSettingsFileName
+-
+-  if (! IN_ECLIPSE) {
+-    inputs.properties(jalviewjsJ2sProps)
+-    outputs.file(jalviewjsJ2sAltSettingsFileName)
+-  }
+-}
+-
+-
+-task jalviewjsEclipseSetup {
+-  dependsOn jalviewjsEclipseCopyDropins
+-  dependsOn jalviewjsSetEclipseWorkspace
+-  dependsOn jalviewjsCreateJ2sSettings
+-}
+-
+-
+-task jalviewjsSyncAllLibs (type: Sync) {
+-  dependsOn jalviewjsTransferUnzipAllLibs
+-  def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteLibDir}")
+-  inputFiles += fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}")
+-  def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
+-
+-  from inputFiles
+-  into outputDir
+-  def outputFiles = []
+-  rename { filename ->
+-    outputFiles += "${outputDir}/${filename}"
+-    null
+-  }
+-  preserve {
+-    include "**"
+-  }
+-  outputs.files outputFiles
+-  inputs.files inputFiles
+-}
+-
+-
+-task jalviewjsSyncResources (type: Sync) {
+-  dependsOn buildResources
+-
+-  def inputFiles = fileTree(dir: resourcesBuildDir)
+-  def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
+-
+-  from inputFiles
+-  into outputDir
+-  def outputFiles = []
+-  rename { filename ->
+-    outputFiles += "${outputDir}/${filename}"
+-    null
+-  }
+-  preserve {
+-    include "**"
+-  }
+-  outputs.files outputFiles
+-  inputs.files inputFiles
+-}
+-
+-
+-task jalviewjsSyncSiteResources (type: Sync) {
+-  def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjs_site_resource_dir}")
+-  def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
+-
+-  from inputFiles
+-  into outputDir
+-  def outputFiles = []
+-  rename { filename ->
+-    outputFiles += "${outputDir}/${filename}"
+-    null
+-  }
+-  preserve {
+-    include "**"
+-  }
+-  outputs.files outputFiles
+-  inputs.files inputFiles
+-}
+-
+-
+-task jalviewjsSyncBuildProperties (type: Sync) {
+-  dependsOn createBuildProperties
+-  def inputFiles = [file(buildProperties)]
+-  def outputDir = "${jalviewDir}/${jalviewjsSiteDir}/${jalviewjs_j2s_subdir}"
+-
+-  from inputFiles
+-  into outputDir
+-  def outputFiles = []
+-  rename { filename ->
+-    outputFiles += "${outputDir}/${filename}"
+-    null
+-  }
+-  preserve {
+-    include "**"
+-  }
+-  outputs.files outputFiles
+-  inputs.files inputFiles
+-}
+-
+-
+-task jalviewjsProjectImport(type: Exec) {
+-  dependsOn eclipseSetup
+-  dependsOn jalviewjsEclipsePaths
+-  dependsOn jalviewjsEclipseSetup
+-
+-  doFirst {
+-    // do not run a headless import when we claim to be in Eclipse
+-    if (IN_ECLIPSE) {
+-      println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+-      throw new StopExecutionException("Not running headless import whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
+-    } else {
+-      println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+-    }
+-  }
+-  //def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview/org.eclipse.jdt.core"
+-  def projdir = eclipseWorkspace.getPath()+"/.metadata/.plugins/org.eclipse.core.resources/.projects/jalview"
+-  executable(eclipseBinary)
+-  args(["-nosplash", "--launcher.suppressErrors", "-application", "com.seeq.eclipse.importprojects.headlessimport", "-data", eclipseWorkspace.getPath(), "-import", jalviewDirAbsolutePath])
+-  if (eclipseDebug) {
+-    args += "-debug"
+-  }
+-  args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
+-  if (!IN_ECLIPSE) {
+-    args += [ "-D${j2sHeadlessBuildProperty}=true" ]
+-    args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
+-  }
+-
+-  inputs.file("${jalviewDir}/.project")
+-  outputs.upToDateWhen { 
+-    file(projdir).exists()
+-  }
+-}
+-
+-
+-task jalviewjsTranspile(type: Exec) {
+-  dependsOn jalviewjsEclipseSetup 
+-  dependsOn jalviewjsProjectImport
+-  dependsOn jalviewjsEclipsePaths
+-  if (!IN_ECLIPSE) {
+-    dependsOn jalviewjsEnableAltFileProperty
+-  }
+-
+-  doFirst {
+-    // do not run a headless transpile when we claim to be in Eclipse
+-    if (IN_ECLIPSE) {
+-      println("Skipping task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+-      throw new StopExecutionException("Not running headless transpile whilst IN_ECLIPSE is '${IN_ECLIPSE}'")
+-    } else {
+-      println("Running task ${name} as IN_ECLIPSE=${IN_ECLIPSE}")
+-    }
+-  }
+-
+-  executable(eclipseBinary)
+-  args(["-nosplash", "--launcher.suppressErrors", "-application", "org.eclipse.jdt.apt.core.aptBuild", "-data", eclipseWorkspace, "-${jalviewjs_eclipse_build_arg}", eclipse_project_name ])
+-  if (eclipseDebug) {
+-    args += "-debug"
+-  }
+-  args += [ "--launcher.appendVmargs", "-vmargs", "-Dorg.eclipse.equinox.p2.reconciler.dropins.directory=${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_eclipse_tmp_dropins_dir}" ]
+-  if (!IN_ECLIPSE) {
+-    args += [ "-D${j2sHeadlessBuildProperty}=true" ]
+-    args += [ "-D${jalviewjs_j2s_alt_file_property}=${jalviewjsJ2sAltSettingsFileName}" ]
+-  }
+-
+-  def stdout
+-  def stderr
+-  doFirst {
+-    stdout = new ByteArrayOutputStream()
+-    stderr = new ByteArrayOutputStream()
+-
+-    def logOutFileName = "${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}"
+-    def logOutFile = file(logOutFileName)
+-    logOutFile.createNewFile()
+-    logOutFile.text = """ROOT: ${jalviewjs_eclipse_root}
+-BINARY: ${eclipseBinary}
+-VERSION: ${eclipseVersion}
+-WORKSPACE: ${eclipseWorkspace}
+-DEBUG: ${eclipseDebug}
+-----
+-"""
+-    def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
+-    // combine stdout and stderr
+-    def logErrFOS = logOutFOS
+-
+-    if (jalviewjs_j2s_to_console.equals("true")) {
+-      standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
+-        new org.apache.tools.ant.util.TeeOutputStream(
+-          logOutFOS,
+-          stdout),
+-        System.out)
+-      errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
+-        new org.apache.tools.ant.util.TeeOutputStream(
+-          logErrFOS,
+-          stderr),
+-        System.err)
+-    } else {
+-      standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
+-        logOutFOS,
+-        stdout)
+-      errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
+-        logErrFOS,
+-        stderr)
+-    }
+-  }
+-
+-  doLast {
+-    if (stdout.toString().contains("Error processing ")) {
+-      // j2s did not complete transpile
+-      //throw new TaskExecutionException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
+-      if (jalviewjs_ignore_transpile_errors.equals("true")) {
+-        println("IGNORING TRANSPILE ERRORS")
+-        println("See eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
+-      } else {
+-        throw new GradleException("Error during transpilation:\n${stderr}\nSee eclipse transpile log file '${jalviewDir}/${jalviewjsBuildDir}/${jalviewjs_j2s_transpile_stdout}'")
+-      }
+-    }
+-  }
+-
+-  inputs.dir("${jalviewDir}/${sourceDir}")
+-  outputs.dir("${jalviewDir}/${jalviewjsTransferSiteJsDir}")
+-  outputs.upToDateWhen( { file("${jalviewDir}/${jalviewjsTransferSiteJsDir}${jalviewjs_server_resource}").exists() } )
+-}
+-
+-
+-def jalviewjsCallCore(String name, FileCollection list, String prefixFile, String suffixFile, String jsfile, String zjsfile, File logOutFile, Boolean logOutConsole) {
+-
+-  def stdout = new ByteArrayOutputStream()
+-  def stderr = new ByteArrayOutputStream()
+-
+-  def coreFile = file(jsfile)
+-  def msg = ""
+-  msg = "Creating core for ${name}...\nGenerating ${jsfile}"
+-  println(msg)
+-  logOutFile.createNewFile()
+-  logOutFile.append(msg+"\n")
+-
+-  def coreTop = file(prefixFile)
+-  def coreBottom = file(suffixFile)
+-  coreFile.getParentFile().mkdirs()
+-  coreFile.createNewFile()
+-  coreFile.write( coreTop.getText("UTF-8") )
+-  list.each {
+-    f ->
+-    if (f.exists()) {
+-      def t = f.getText("UTF-8")
+-      t.replaceAll("Clazz\\.([^_])","Clazz_${1}")
+-      coreFile.append( t )
+-    } else {
+-      msg = "...file '"+f.getPath()+"' does not exist, skipping"
+-      println(msg)
+-      logOutFile.append(msg+"\n")
+-    }
+-  }
+-  coreFile.append( coreBottom.getText("UTF-8") )
+-
+-  msg = "Generating ${zjsfile}"
+-  println(msg)
+-  logOutFile.append(msg+"\n")
+-  def logOutFOS = new FileOutputStream(logOutFile, true) // true == append
+-  def logErrFOS = logOutFOS
+-
+-  javaexec {
+-    classpath = files(["${jalviewDir}/${jalviewjs_closure_compiler}"])
+-    main = "com.google.javascript.jscomp.CommandLineRunner"
+-    jvmArgs = [ "-Dfile.encoding=UTF-8" ]
+-    args = [ "--compilation_level", "SIMPLE_OPTIMIZATIONS", "--warning_level", "QUIET", "--charset", "UTF-8", "--js", jsfile, "--js_output_file", zjsfile ]
+-    maxHeapSize = "2g"
+-
+-    msg = "\nRunning '"+commandLine.join(' ')+"'\n"
+-    println(msg)
+-    logOutFile.append(msg+"\n")
+-
+-    if (logOutConsole) {
+-      standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
+-        new org.apache.tools.ant.util.TeeOutputStream(
+-          logOutFOS,
+-          stdout),
+-        standardOutput)
+-        errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
+-          new org.apache.tools.ant.util.TeeOutputStream(
+-            logErrFOS,
+-            stderr),
+-          errorOutput)
+-    } else {
+-      standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
+-        logOutFOS,
+-        stdout)
+-        errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
+-          logErrFOS,
+-          stderr)
+-    }
+-  }
+-  msg = "--"
+-  println(msg)
+-  logOutFile.append(msg+"\n")
+-}
+-
+-
+-task jalviewjsBuildAllCores {
+-  group "JalviewJS"
+-  description "Build the core js lib closures listed in the classlists dir"
+-  dependsOn jalviewjsTranspile
+-  dependsOn jalviewjsTransferUnzipSwingJs
+-
+-  def j2sDir = "${jalviewDir}/${jalviewjsTransferSiteJsDir}/${jalviewjs_j2s_subdir}"
+-  def swingJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_j2s_subdir}"
+-  def libJ2sDir = "${jalviewDir}/${jalviewjsTransferSiteLibDir}/${jalviewjs_j2s_subdir}"
+-  def jsDir = "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}/${jalviewjs_js_subdir}"
+-  def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}/${jalviewjs_j2s_subdir}/core"
+-  def prefixFile = "${jsDir}/core/coretop2.js"
+-  def suffixFile = "${jsDir}/core/corebottom2.js"
+-
+-  inputs.file prefixFile
+-  inputs.file suffixFile
+-
+-  def classlistFiles = []
+-  // add the classlists found int the jalviewjs_classlists_dir
+-  fileTree(dir: "${jalviewDir}/${jalviewjs_classlists_dir}", include: "*.txt").each {
+-    file ->
+-    def name = file.getName() - ".txt"
+-    classlistFiles += [
+-      'file': file,
+-      'name': name
+-    ]
+-  }
+-
+-  // _jmol and _jalview cores. Add any other peculiar classlist.txt files here
+-  //classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jmol}"), 'name': "_jvjmol" ]
+-  classlistFiles += [ 'file': file("${jalviewDir}/${jalviewjs_classlist_jalview}"), 'name': jalviewjsJalviewCoreName ]
+-
+-  jalviewjsCoreClasslists = []
+-
+-  classlistFiles.each {
+-    hash ->
+-
+-    def file = hash['file']
+-    if (! file.exists()) {
+-      //println("...classlist file '"+file.getPath()+"' does not exist, skipping")
+-      return false // this is a "continue" in groovy .each closure
+-    }
+-    def name = hash['name']
+-    if (name == null) {
+-      name = file.getName() - ".txt"
+-    }
+-
+-    def filelist = []
+-    file.eachLine {
+-      line ->
+-        filelist += line
+-    }
+-    def list = fileTree(dir: j2sDir, includes: filelist)
+-
+-    def jsfile = "${outputDir}/core${name}.js"
+-    def zjsfile = "${outputDir}/core${name}.z.js"
+-
+-    jalviewjsCoreClasslists += [
+-      'jsfile': jsfile,
+-      'zjsfile': zjsfile,
+-      'list': list,
+-      'name': name
+-    ]
+-
+-    inputs.file(file)
+-    inputs.files(list)
+-    outputs.file(jsfile)
+-    outputs.file(zjsfile)
+-  }
+-  
+-  // _stevesoft core. add any cores without a classlist here (and the inputs and outputs)
+-  def stevesoftClasslistName = "_stevesoft"
+-  def stevesoftClasslist = [
+-    'jsfile': "${outputDir}/core${stevesoftClasslistName}.js",
+-    'zjsfile': "${outputDir}/core${stevesoftClasslistName}.z.js",
+-    'list': fileTree(dir: j2sDir, include: "com/stevesoft/pat/**/*.js"),
+-    'name': stevesoftClasslistName
+-  ]
+-  jalviewjsCoreClasslists += stevesoftClasslist
+-  inputs.files(stevesoftClasslist['list'])
+-  outputs.file(stevesoftClasslist['jsfile'])
+-  outputs.file(stevesoftClasslist['zjsfile'])
+-
+-  // _all core
+-  def allClasslistName = "_all"
+-  def allJsFiles = fileTree(dir: j2sDir, include: "**/*.js")
+-  allJsFiles += fileTree(
+-    dir: libJ2sDir,
+-    include: "**/*.js",
+-    excludes: [
+-      // these exlusions are files that the closure-compiler produces errors for. Should fix them
+-      "**/org/jmol/jvxl/readers/IsoIntersectFileReader.js",
+-      "**/org/jmol/export/JSExporter.js"
+-    ]
+-  )
+-  allJsFiles += fileTree(
+-    dir: swingJ2sDir,
+-    include: "**/*.js",
+-    excludes: [
+-      // these exlusions are files that the closure-compiler produces errors for. Should fix them
+-      "**/sun/misc/Unsafe.js",
+-      "**/swingjs/jquery/jquery-editable-select.js",
+-      "**/swingjs/jquery/j2sComboBox.js",
+-      "**/sun/misc/FloatingDecimal.js"
+-    ]
+-  )
+-  def allClasslist = [
+-    'jsfile': "${outputDir}/core${allClasslistName}.js",
+-    'zjsfile': "${outputDir}/core${allClasslistName}.z.js",
+-    'list': allJsFiles,
+-    'name': allClasslistName
+-  ]
+-  // not including this version of "all" core at the moment
+-  //jalviewjsCoreClasslists += allClasslist
+-  inputs.files(allClasslist['list'])
+-  outputs.file(allClasslist['jsfile'])
+-  outputs.file(allClasslist['zjsfile'])
+-
+-  doFirst {
+-    def logOutFile = file("${jalviewDirAbsolutePath}/${jalviewjsBuildDir}/${jalviewjs_j2s_closure_stdout}")
+-    logOutFile.getParentFile().mkdirs()
+-    logOutFile.createNewFile()
+-    logOutFile.write(getDate("yyyy-MM-dd HH:mm:ss")+" jalviewjsBuildAllCores\n----\n")
+-
+-    jalviewjsCoreClasslists.each {
+-      jalviewjsCallCore(it.name, it.list, prefixFile, suffixFile, it.jsfile, it.zjsfile, logOutFile, jalviewjs_j2s_to_console.equals("true"))
+-    }
+-  }
+-
+-}
+-
+-
+-def jalviewjsPublishCoreTemplate(String coreName, String templateName, File inputFile, String outputFile) {
+-  copy {
+-    from inputFile
+-    into file(outputFile).getParentFile()
+-    rename { filename ->
+-      if (filename.equals(inputFile.getName())) {
+-        return file(outputFile).getName()
+-      }
+-      return null
+-    }
+-    filter(ReplaceTokens,
+-      beginToken: '_',
+-      endToken: '_',
+-      tokens: [
+-        'MAIN': '"'+main_class+'"',
+-        'CODE': "null",
+-        'NAME': jalviewjsJalviewTemplateName+" [core ${coreName}]",
+-        'COREKEY': jalviewjs_core_key,
+-        'CORENAME': coreName
+-      ]
+-    )
+-  }
+-}
+-
+-
+-task jalviewjsPublishCoreTemplates {
+-  dependsOn jalviewjsBuildAllCores
+-  def inputFileName = "${jalviewDir}/${j2s_coretemplate_html}"
+-  def inputFile = file(inputFileName)
+-  def outputDir = "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
+-
+-  def outputFiles = []
+-  jalviewjsCoreClasslists.each { cl ->
+-    def outputFile = "${outputDir}/${jalviewjsJalviewTemplateName}_${cl.name}.html"
+-    cl['outputfile'] = outputFile
+-    outputFiles += outputFile
+-  }
+-
+-  doFirst {
+-    jalviewjsCoreClasslists.each { cl ->
+-      jalviewjsPublishCoreTemplate(cl.name, jalviewjsJalviewTemplateName, inputFile, cl.outputfile)
+-    }
+-  }
+-  inputs.file(inputFile)
+-  outputs.files(outputFiles)
+-}
+-
+-
+-task jalviewjsSyncCore (type: Sync) {
+-  dependsOn jalviewjsBuildAllCores
+-  dependsOn jalviewjsPublishCoreTemplates
+-  def inputFiles = fileTree(dir: "${jalviewDir}/${jalviewjsTransferSiteCoreDir}")
+-  def outputDir = "${jalviewDir}/${jalviewjsSiteDir}"
+-
+-  from inputFiles
+-  into outputDir
+-  def outputFiles = []
+-  rename { filename ->
+-    outputFiles += "${outputDir}/${filename}"
+-    null
+-  }
+-  preserve {
+-    include "**"
+-  }
+-  outputs.files outputFiles
+-  inputs.files inputFiles
+-}
+-
+-
+-// this Copy version of TransferSiteJs will delete anything else in the target dir
+-task jalviewjsCopyTransferSiteJs(type: Copy) {
+-  dependsOn jalviewjsTranspile
+-  from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
+-  into "${jalviewDir}/${jalviewjsSiteDir}"
+-}
+-
+-
+-// this Sync version of TransferSite is used by buildship to keep the website automatically up to date when a file changes
+-task jalviewjsSyncTransferSiteJs(type: Sync) {
+-  from "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
+-  include "**/*.*"
+-  into "${jalviewDir}/${jalviewjsSiteDir}"
+-  preserve {
+-    include "**"
+-  }
+-}
+-
+-
+-jalviewjsSyncAllLibs.mustRunAfter jalviewjsCopyTransferSiteJs
+-jalviewjsSyncResources.mustRunAfter jalviewjsCopyTransferSiteJs
+-jalviewjsSyncSiteResources.mustRunAfter jalviewjsCopyTransferSiteJs
+-jalviewjsSyncBuildProperties.mustRunAfter jalviewjsCopyTransferSiteJs
+-
+-jalviewjsSyncAllLibs.mustRunAfter jalviewjsSyncTransferSiteJs
+-jalviewjsSyncResources.mustRunAfter jalviewjsSyncTransferSiteJs
+-jalviewjsSyncSiteResources.mustRunAfter jalviewjsSyncTransferSiteJs
+-jalviewjsSyncBuildProperties.mustRunAfter jalviewjsSyncTransferSiteJs
+-
+-
+-task jalviewjsPrepareSite {
+-  group "JalviewJS"
+-  description "Prepares the website folder including unzipping files and copying resources"
+-  dependsOn jalviewjsSyncAllLibs
+-  dependsOn jalviewjsSyncResources
+-  dependsOn jalviewjsSyncSiteResources
+-  dependsOn jalviewjsSyncBuildProperties
+-  dependsOn jalviewjsSyncCore
+-}
+-
+-
+-task jalviewjsBuildSite {
+-  group "JalviewJS"
+-  description "Builds the whole website including transpiled code"
+-  dependsOn jalviewjsCopyTransferSiteJs
+-  dependsOn jalviewjsPrepareSite
+-}
+-
+-
+-task cleanJalviewjsTransferSite {
+-  doFirst {
+-    delete "${jalviewDir}/${jalviewjsTransferSiteJsDir}"
+-    delete "${jalviewDir}/${jalviewjsTransferSiteLibDir}"
+-    delete "${jalviewDir}/${jalviewjsTransferSiteSwingJsDir}"
+-    delete "${jalviewDir}/${jalviewjsTransferSiteCoreDir}"
+-  }
+-}
+-
+-
+-task cleanJalviewjsSite {
+-  dependsOn cleanJalviewjsTransferSite
+-  doFirst {
+-    delete "${jalviewDir}/${jalviewjsSiteDir}"
+-  }
+-}
+-
+-
+-task jalviewjsSiteTar(type: Tar) {
+-  group "JalviewJS"
+-  description "Creates a tar.gz file for the website"
+-  dependsOn jalviewjsBuildSite
+-  def outputFilename = "jalviewjs-site-${JALVIEW_VERSION}.tar.gz"
+-  archiveFileName = outputFilename
+-
+-  compression Compression.GZIP
+-
+-  from "${jalviewDir}/${jalviewjsSiteDir}"
+-  into jalviewjs_site_dir // this is inside the tar file
+-
+-  inputs.dir("${jalviewDir}/${jalviewjsSiteDir}")
+-}
+-
+-
+-task jalviewjsServer {
+-  group "JalviewJS"
+-  def filename = "jalviewjsTest.html"
+-  description "Starts a webserver on localhost to test the website. See ${filename} to access local site on most recently used port."
+-  def htmlFile = "${jalviewDirAbsolutePath}/${filename}"
+-  doLast {
+-
+-    def factory
+-    try {
+-      def f = Class.forName("org.gradle.plugins.javascript.envjs.http.simple.SimpleHttpFileServerFactory")
+-      factory = f.newInstance()
+-    } catch (ClassNotFoundException e) {
+-      throw new GradleException("Unable to create SimpleHttpFileServerFactory")
+-    }
+-    def port = Integer.valueOf(jalviewjs_server_port)
+-    def start = port
+-    def running = false
+-    def url
+-    def jalviewjsServer
+-    while(port < start+1000 && !running) {
+-      try {
+-        def doc_root = new File("${jalviewDirAbsolutePath}/${jalviewjsSiteDir}")
+-        jalviewjsServer = factory.start(doc_root, port)
+-        running = true
+-        url = jalviewjsServer.getResourceUrl(jalviewjs_server_resource)
+-        println("SERVER STARTED with document root ${doc_root}.")
+-        println("Go to "+url+" . Run  gradle --stop  to stop (kills all gradle daemons).")
+-        println("For debug: "+url+"?j2sdebug")
+-        println("For verbose: "+url+"?j2sverbose")
+-      } catch (Exception e) {
+-        port++;
+-      }
+-    }
+-    def htmlText = """
+-      <p><a href="${url}">JalviewJS Test. &lt;${url}&gt;</a></p>
+-      <p><a href="${url}?j2sdebug">JalviewJS Test with debug. &lt;${url}?j2sdebug&gt;</a></p>
+-      <p><a href="${url}?j2sverbose">JalviewJS Test with verbose. &lt;${url}?j2sdebug&gt;</a></p>
+-      """
+-    jalviewjsCoreClasslists.each { cl ->
+-      def urlcore = jalviewjsServer.getResourceUrl(file(cl.outputfile).getName())
+-      htmlText += """
+-      <p><a href="${urlcore}">${jalviewjsJalviewTemplateName} [core ${cl.name}]. &lt;${urlcore}&gt;</a></p>
+-      """
+-      println("For core ${cl.name}: "+urlcore)
+-    }
+-
+-    file(htmlFile).text = htmlText
+-  }
+-
+-  outputs.file(htmlFile)
+-  outputs.upToDateWhen({false})
+-}
+-
+-
+-task cleanJalviewjsAll {
+-  group "JalviewJS"
+-  description "Delete all configuration and build artifacts to do with JalviewJS build"
+-  dependsOn cleanJalviewjsSite
+-  dependsOn jalviewjsEclipsePaths
+-  
+-  doFirst {
+-    delete "${jalviewDir}/${jalviewjsBuildDir}"
+-    delete "${jalviewDir}/${eclipse_bin_dir}"
+-    if (eclipseWorkspace != null && file(eclipseWorkspace.getAbsolutePath()+"/.metadata").exists()) {
+-      delete file(eclipseWorkspace.getAbsolutePath()+"/.metadata")
+-    }
+-    delete jalviewjsJ2sAltSettingsFileName
+-  }
+-
+-  outputs.upToDateWhen( { false } )
+-}
+-
+-
+-task jalviewjsIDE_checkJ2sPlugin {
+-  group "00 JalviewJS in Eclipse"
+-  description "Compare the swingjs/net.sf.j2s.core(-j11)?.jar file with the Eclipse IDE's plugin version (found in the 'dropins' dir)"
+-
+-  doFirst {
+-    def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
+-    def j2sPluginFile = file(j2sPlugin)
+-    def eclipseHome = System.properties["eclipse.home.location"]
+-    if (eclipseHome == null || ! IN_ECLIPSE) {
+-      throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. Skipping J2S Plugin Check.")
+-    }
+-    def eclipseJ2sPluginDirs = [ "${eclipseHome}/dropins" ]
+-    def altPluginsDir = System.properties["org.eclipse.equinox.p2.reconciler.dropins.directory"]
+-    if (altPluginsDir != null && file(altPluginsDir).exists()) {
+-      eclipseJ2sPluginDirs += altPluginsDir
+-    }
+-    def foundPlugin = false
+-    def j2sPluginFileName = j2sPluginFile.getName()
+-    def eclipseJ2sPlugin
+-    def eclipseJ2sPluginFile
+-    eclipseJ2sPluginDirs.any { dir ->
+-      eclipseJ2sPlugin = "${dir}/${j2sPluginFileName}"
+-      eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
+-      if (eclipseJ2sPluginFile.exists()) {
+-        foundPlugin = true
+-        return true
+-      }
+-    }
+-    if (!foundPlugin) {
+-      def msg = "Eclipse J2S Plugin is not installed (could not find '${j2sPluginFileName}' in\n"+eclipseJ2sPluginDirs.join("\n")+"\n)\nTry running task jalviewjsIDE_copyJ2sPlugin"
+-      System.err.println(msg)
+-      throw new StopExecutionException(msg)
+-    }
+-
+-    def digest = MessageDigest.getInstance("MD5")
+-
+-    digest.update(j2sPluginFile.text.bytes)
+-    def j2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
+-
+-    digest.update(eclipseJ2sPluginFile.text.bytes)
+-    def eclipseJ2sPluginMd5 = new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
+-     
+-    if (j2sPluginMd5 != eclipseJ2sPluginMd5) {
+-      def msg = "WARNING! Eclipse J2S Plugin '${eclipseJ2sPlugin}' is different to this commit's version '${j2sPlugin}'"
+-      System.err.println(msg)
+-      throw new StopExecutionException(msg)
+-    } else {
+-      def msg = "Eclipse J2S Plugin '${eclipseJ2sPlugin}' is the same as '${j2sPlugin}' (this is good)"
+-      println(msg)
+-    }
+-  }
+-}
+-
+-task jalviewjsIDE_copyJ2sPlugin {
+-  group "00 JalviewJS in Eclipse"
+-  description "Copy the swingjs/net.sf.j2s.core(-j11)?.jar file into the Eclipse IDE's 'dropins' dir"
+-
+-  doFirst {
+-    def j2sPlugin = string("${jalviewDir}/${jalviewjsJ2sPlugin}")
+-    def j2sPluginFile = file(j2sPlugin)
+-    def eclipseHome = System.properties["eclipse.home.location"]
+-    if (eclipseHome == null || ! IN_ECLIPSE) {
+-      throw new StopExecutionException("Cannot find running Eclipse home from System.properties['eclipse.home.location']. NOT copying J2S Plugin.")
+-    }
+-    def eclipseJ2sPlugin = "${eclipseHome}/dropins/${j2sPluginFile.getName()}"
+-    def eclipseJ2sPluginFile = file(eclipseJ2sPlugin)
+-    def msg = "WARNING! Copying this commit's j2s plugin '${j2sPlugin}' to Eclipse J2S Plugin '${eclipseJ2sPlugin}'\n* May require an Eclipse restart"
+-    System.err.println(msg)
+-    copy {
+-      from j2sPlugin
+-      eclipseJ2sPluginFile.getParentFile().mkdirs()
+-      into eclipseJ2sPluginFile.getParent()
+-    }
+-  }
+-}
+-
+-
+-task jalviewjsIDE_j2sFile {
+-  group "00 JalviewJS in Eclipse"
+-  description "Creates the .j2s file"
+-  dependsOn jalviewjsCreateJ2sSettings
+-}
+-
+-
+-task jalviewjsIDE_SyncCore {
+-  group "00 JalviewJS in Eclipse"
+-  description "Build the core js lib closures listed in the classlists dir and publish core html from template"
+-  dependsOn jalviewjsSyncCore
+-}
+-
+-
+-task jalviewjsIDE_SyncSiteAll {
+-  dependsOn jalviewjsSyncAllLibs
+-  dependsOn jalviewjsSyncResources
+-  dependsOn jalviewjsSyncSiteResources
+-  dependsOn jalviewjsSyncBuildProperties
+-}
+-
+-
+-cleanJalviewjsTransferSite.mustRunAfter jalviewjsIDE_SyncSiteAll
+-
+-
+-task jalviewjsIDE_PrepareSite {
+-  group "00 JalviewJS in Eclipse"
+-  description "Sync libs and resources to site dir, but not closure cores"
+-
+-  dependsOn jalviewjsIDE_SyncSiteAll
+-  //dependsOn cleanJalviewjsTransferSite // not sure why this clean is here -- will slow down a re-run of this task
+-}
+-
+-
+-task jalviewjsIDE_AssembleSite {
+-  group "00 JalviewJS in Eclipse"
+-  description "Assembles unzipped supporting zipfiles, resources, site resources and closure cores into the Eclipse transpiled site"
+-  dependsOn jalviewjsPrepareSite
+-}
+-
+-
+-task jalviewjsIDE_SiteClean {
+-  group "00 JalviewJS in Eclipse"
+-  description "Deletes the Eclipse transpiled site"
+-  dependsOn cleanJalviewjsSite
+-}
+-
+-
+-task jalviewjsIDE_Server {
+-  group "00 JalviewJS in Eclipse"
+-  description "Starts a webserver on localhost to test the website"
+-  dependsOn jalviewjsServer
+-}
+-
+-
+-// buildship runs this at import or gradle refresh
+-task eclipseSynchronizationTask {
+-  //dependsOn eclipseSetup
+-  dependsOn createBuildProperties
+-  if (J2S_ENABLED) {
+-    dependsOn jalviewjsIDE_j2sFile
+-    dependsOn jalviewjsIDE_checkJ2sPlugin
+-    dependsOn jalviewjsIDE_PrepareSite
+-  }
+-}
+-
+-
+-// buildship runs this at build time or project refresh
+-task eclipseAutoBuildTask {
+-  //dependsOn jalviewjsIDE_checkJ2sPlugin
+-  //dependsOn jalviewjsIDE_PrepareSite
++  outputs.file("${outputDir}/${archiveFileName}")
+ }
+-
+-task jalviewjs {
+-  group "JalviewJS"
+-  description "Build the site"
+-  dependsOn jalviewjsBuildSite
+-}
diff --git a/utils/debian/debian/icons/128x128/apps/jalview-icon.png b/utils/debian/debian/icons/128x128/apps/jalview-icon.png
new file mode 100644 (file)
index 0000000..83a15fb
Binary files /dev/null and b/utils/debian/debian/icons/128x128/apps/jalview-icon.png differ
diff --git a/utils/debian/debian/icons/16x16/apps/jalview-icon.png b/utils/debian/debian/icons/16x16/apps/jalview-icon.png
new file mode 100644 (file)
index 0000000..120bbf8
Binary files /dev/null and b/utils/debian/debian/icons/16x16/apps/jalview-icon.png differ
diff --git a/utils/debian/debian/icons/16x16/mimetypes/x-jalview-file.png b/utils/debian/debian/icons/16x16/mimetypes/x-jalview-file.png
new file mode 100644 (file)
index 0000000..af2b2c3
Binary files /dev/null and b/utils/debian/debian/icons/16x16/mimetypes/x-jalview-file.png differ
diff --git a/utils/debian/debian/icons/22x22/mimetypes/x-jalview-file.png b/utils/debian/debian/icons/22x22/mimetypes/x-jalview-file.png
new file mode 100644 (file)
index 0000000..f5ac4ba
Binary files /dev/null and b/utils/debian/debian/icons/22x22/mimetypes/x-jalview-file.png differ
diff --git a/utils/debian/debian/icons/24x24/mimetypes/x-jalview-file.png b/utils/debian/debian/icons/24x24/mimetypes/x-jalview-file.png
new file mode 100644 (file)
index 0000000..7d9b615
Binary files /dev/null and b/utils/debian/debian/icons/24x24/mimetypes/x-jalview-file.png differ
diff --git a/utils/debian/debian/icons/256x256/apps/jalview-icon.png b/utils/debian/debian/icons/256x256/apps/jalview-icon.png
new file mode 100644 (file)
index 0000000..2dc68b0
Binary files /dev/null and b/utils/debian/debian/icons/256x256/apps/jalview-icon.png differ
diff --git a/utils/debian/debian/icons/32x32/apps/jalview-icon.png b/utils/debian/debian/icons/32x32/apps/jalview-icon.png
new file mode 100644 (file)
index 0000000..8f8c3f6
Binary files /dev/null and b/utils/debian/debian/icons/32x32/apps/jalview-icon.png differ
diff --git a/utils/debian/debian/icons/32x32/mimetypes/x-jalview-file.png b/utils/debian/debian/icons/32x32/mimetypes/x-jalview-file.png
new file mode 100644 (file)
index 0000000..1a6eb7c
Binary files /dev/null and b/utils/debian/debian/icons/32x32/mimetypes/x-jalview-file.png differ
diff --git a/utils/debian/debian/icons/48x48/apps/jalview-icon.png b/utils/debian/debian/icons/48x48/apps/jalview-icon.png
new file mode 100644 (file)
index 0000000..b4837fa
Binary files /dev/null and b/utils/debian/debian/icons/48x48/apps/jalview-icon.png differ
diff --git a/utils/debian/debian/icons/48x48/mimetypes/x-jalview-file.png b/utils/debian/debian/icons/48x48/mimetypes/x-jalview-file.png
new file mode 100644 (file)
index 0000000..bf482ec
Binary files /dev/null and b/utils/debian/debian/icons/48x48/mimetypes/x-jalview-file.png differ
diff --git a/utils/debian/debian/icons/512x512/apps/jalview-icon.png b/utils/debian/debian/icons/512x512/apps/jalview-icon.png
new file mode 100644 (file)
index 0000000..41b845b
Binary files /dev/null and b/utils/debian/debian/icons/512x512/apps/jalview-icon.png differ
diff --git a/utils/debian/debian/icons/512x512/mimetypes/x-jalview-file.png b/utils/debian/debian/icons/512x512/mimetypes/x-jalview-file.png
new file mode 100644 (file)
index 0000000..38edb48
Binary files /dev/null and b/utils/debian/debian/icons/512x512/mimetypes/x-jalview-file.png differ
diff --git a/utils/debian/debian/icons/64x64/apps/jalview-icon.png b/utils/debian/debian/icons/64x64/apps/jalview-icon.png
new file mode 100644 (file)
index 0000000..2490a8d
Binary files /dev/null and b/utils/debian/debian/icons/64x64/apps/jalview-icon.png differ
diff --git a/utils/debian/debian/jalview-mailcap b/utils/debian/debian/jalview-mailcap
new file mode 100644 (file)
index 0000000..5be7ab9
--- /dev/null
@@ -0,0 +1,22 @@
+application/x-jalview+xml+zip; jalview -open '%s'; description="Jalview File"; nametemplate=%s.jvp; test=test -n "$DISPLAY"; priority=10
+chemical/x-cif; jalview -open '%s'; description="CIF File"; nametemplate=%s.cif; test=test -n "$DISPLAY"; priority=4
+chemical/x-mmcif; jalview -open '%s'; description="mmCIF File"; nametemplate=%s.mcif; test=test -n "$DISPLAY"; priority=4
+chemical/x-pdb; jalview -open '%s'; description="PDB File"; nametemplate=%s.pdb; test=test -n "$DISPLAY"; priority=4
+application/x-amsa+txt; jalview -open '%s'; description="AMSA File"; nametemplate=%s.amsa; test=test -n "$DISPLAY"; priority=9
+application/x-jalview-annotations+text; jalview -open '%s'; description="Jalview Annotations File"; nametemplate=%s.annotations; test=test -n "$DISPLAY"; priority=10
+application/x-jalview-biojson+json; jalview -open '%s'; description="BioJSON File"; nametemplate=%s.biojson; test=test -n "$DISPLAY"; priority=10
+application/x-blc+txt; jalview -open '%s'; description="BLC File"; nametemplate=%s.blc; test=test -n "$DISPLAY"; priority=9
+application/x-clustal+txt; jalview -open '%s'; description="Clustal File"; nametemplate=%s.aln; test=test -n "$DISPLAY"; priority=9
+application/x-fasta+txt; jalview -open '%s'; description="Fasta File"; nametemplate=%s.fa; test=test -n "$DISPLAY"; priority=9
+application/x-jalview-features+text; jalview -open '%s'; description="Jalview Features File"; nametemplate=%s.features; test=test -n "$DISPLAY"; priority=10
+application/x-gff2+txt; jalview -open '%s'; description="Generic Features Format v2 File"; nametemplate=%s.gff2; test=test -n "$DISPLAY"; priority=9
+application/x-gff3+txt; jalview -open '%s'; description="Generic Features Format v3 File"; nametemplate=%s.gff3; test=test -n "$DISPLAY"; priority=9
+application/x-jalview-jnet+text; jalview -open '%s'; description="JnetFile File"; nametemplate=%s.concise; test=test -n "$DISPLAY"; priority=10
+application/x-msf+txt; jalview -open '%s'; description="MSF File"; nametemplate=%s.msf; test=test -n "$DISPLAY"; priority=9
+application/x-pfam+txt; jalview -open '%s'; description="PFAM File"; nametemplate=%s.pfam; test=test -n "$DISPLAY"; priority=9
+application/x-phylip+txt; jalview -open '%s'; description="PHYLIP File"; nametemplate=%s.phy; test=test -n "$DISPLAY"; priority=9
+application/x-pileup+txt; jalview -open '%s'; description="PileUp File"; nametemplate=%s.pileup; test=test -n "$DISPLAY"; priority=9
+application/x-pir+txt; jalview -open '%s'; description="PIR File"; nametemplate=%s.pir; test=test -n "$DISPLAY"; priority=9
+application/rnaml+xml; jalview -open '%s'; description="RNAML File"; nametemplate=%s.rnaml; test=test -n "$DISPLAY"; priority=9
+application/x-jalview-scorematrix+text; jalview -open '%s'; description="Substitution Matrix File"; nametemplate=%s.mat; test=test -n "$DISPLAY"; priority=10
+application/x-stockholm+txt; jalview -open '%s'; description="Stockholm File"; nametemplate=%s.sto; test=test -n "$DISPLAY"; priority=9
diff --git a/utils/debian/debian/jalview-mime.xml b/utils/debian/debian/jalview-mime.xml
new file mode 100644 (file)
index 0000000..757682c
--- /dev/null
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+
+  <mime-type type="application/x-jalview+xml+zip">
+    <comment>Jalview File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.jvp" weight="100"/>
+  </mime-type>
+
+  <mime-type type="chemical/x-cif">
+    <comment>CIF File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.cif" weight="40"/>
+  </mime-type>
+
+  <mime-type type="chemical/x-mmcif">
+    <comment>mmCIF File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.mcif" weight="40"/>
+    <glob pattern="*.mmcif" weight="40"/>
+  </mime-type>
+
+  <mime-type type="chemical/x-pdb">
+    <comment>PDB File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.pdb" weight="40"/>
+    <glob pattern="*.ent" weight="40"/>
+  </mime-type>
+
+  <mime-type type="application/x-amsa+txt">
+    <comment>AMSA File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.amsa" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-jalview-annotations+text">
+    <comment>Jalview Annotations File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.annotations" weight="100"/>
+    <glob pattern="*.jvannotations" weight="100"/>
+  </mime-type>
+
+  <mime-type type="application/x-jalview-biojson+json">
+    <comment>BioJSON File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.biojson" weight="100"/>
+  </mime-type>
+
+  <mime-type type="application/x-blc+txt">
+    <comment>BLC File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.blc" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-clustal+txt">
+    <comment>Clustal File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.aln" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-fasta+txt">
+    <comment>Fasta File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.fa" weight="90"/>
+    <glob pattern="*.fasta" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-jalview-features+text">
+    <comment>Jalview Features File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.features" weight="100"/>
+    <glob pattern="*.jvfeatures" weight="100"/>
+  </mime-type>
+
+  <mime-type type="application/x-gff2+txt">
+    <comment>Generic Features Format v2 File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.gff2" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-gff3+txt">
+    <comment>Generic Features Format v3 File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.gff3" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-jalview-jnet+text">
+    <comment>JnetFile File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.concise" weight="100"/>
+    <glob pattern="*.jnet" weight="100"/>
+  </mime-type>
+
+  <mime-type type="application/x-msf+txt">
+    <comment>MSF File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.msf" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-pfam+txt">
+    <comment>PFAM File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.pfam" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-phylip+txt">
+    <comment>PHYLIP File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.phy" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-pileup+txt">
+    <comment>PileUp File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.pileup" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-pir+txt">
+    <comment>PIR File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.pir" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/rnaml+xml">
+    <comment>RNAML File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.rnaml" weight="90"/>
+  </mime-type>
+
+  <mime-type type="application/x-jalview-scorematrix+text">
+    <comment>Substitution Matrix File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.mat" weight="100"/>
+  </mime-type>
+
+  <mime-type type="application/x-stockholm+txt">
+    <comment>Stockholm File</comment>
+    <generic-icon name="x-jalview-file"/>
+    <glob pattern="*.sto" weight="90"/>
+    <glob pattern="*.stk" weight="90"/>
+  </mime-type>
+
+</mime-info>
diff --git a/utils/debian/debian/jalview.desktop b/utils/debian/debian/jalview.desktop
new file mode 100644 (file)
index 0000000..7cf0afb
--- /dev/null
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Version=1.1
+Type=Application
+Name=Jalview
+Comment=Multiple Sequence Alignment Editor
+Icon=jalview-icon
+Type=Application
+TryExec=jalview
+Exec=jalview %u
+Terminal=false
+Categories=Science;Biology;
+Keywords=alignment;sequence;
+MimeType=application/x-jalview+xml+zip;chemical/x-cif;chemical/x-mmcif;chemical/x-pdb;application/x-amsa+txt;application/x-jalview-annotations+text;application/x-jalview-biojson+json;application/x-blc+txt;application/x-clustal+txt;application/x-fasta+txt;application/x-jalview-features+text;application/x-gff2+txt;application/x-gff3+txt;application/x-jalview-jnet+text;application/x-msf+txt;application/x-pfam+txt;application/x-phylip+txt;application/x-pileup+txt;application/x-pir+txt;application/rnaml+xml;application/x-jalview-scorematrix+text;application/x-stockholm+txt
diff --git a/utils/debian/debian/wrappers/jalview b/utils/debian/debian/wrappers/jalview
new file mode 100755 (executable)
index 0000000..4823bee
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+set -e
+ARG1=$1
+
+# copy the debian default settings if no user jalview settings file exist
+if [ -n "${HOME}" -a \! -e ${HOME}/.jalview_properties ]; then
+  /bin/cp /etc/jalview_properties ${HOME}/.jalview_properties
+fi
+
+# check to see if $1 is set and is not start of other cli set args
+OPEN=""
+if [ -n "$ARG1" -a "$ARG1" = "${ARG1#-}" ]; then
+  # first argument exists and does not start with a "-"
+  OPEN="-open"
+fi
+  
+java -jar /usr/share/java/jalview.jar $OPEN "$@"
diff --git a/utils/debian/debian_build.gradle b/utils/debian/debian_build.gradle
new file mode 100644 (file)
index 0000000..a0e8cd9
--- /dev/null
@@ -0,0 +1,514 @@
+/* Convention for properties.  Read from gradle.properties, use lower_case_underlines for property names.
+ * For properties set within build.gradle, use camelCaseNoSpace.
+ */
+import org.apache.tools.ant.filters.ReplaceTokens
+
+plugins {
+  id 'java'
+  id 'application'
+}
+
+// in ext the values are cast to Object. Ensure string values are cast as String (and not GStringImpl) for later use
+def string(Object o) {
+  return o == null ? "" : o.toString()
+}
+
+def overrideProperties(String propsFileName, boolean output = false) {
+  if (propsFileName == null) {
+    return
+  }
+  def propsFile = file(propsFileName)
+  if (propsFile != null && propsFile.exists()) {
+    println("Using properties from file '${propsFileName}'")
+    try {
+      def p = new Properties()
+      def localPropsFIS = new FileInputStream(propsFile)
+      p.load(localPropsFIS)
+      localPropsFIS.close()
+      p.each {
+        key, val -> 
+          def oldval
+          if (project.hasProperty(key)) {
+            oldval = project.findProperty(key)
+            project.setProperty(key, val)
+            if (output) {
+              println("Overriding property '${key}' ('${oldval}') with ${file(propsFile).getName()} value '${val}'")
+            }
+          } else {
+            ext.setProperty(key, val)
+            if (output) {
+              println("Setting ext property '${key}' with ${file(propsFile).getName()}s value '${val}'")
+            }
+          }
+      }
+    } catch (Exception e) {
+      println("Exception reading local.properties")
+      e.printStackTrace()
+    }
+  }
+}
+
+project.ext {
+  jalviewDirAbsolutePath = file(jalviewDir).getAbsolutePath()
+  jalviewDirRelativePath = jalviewDir
+
+  propertiesChannelName = "release"
+  channelDir = string("${jalviewDir}/${channel_properties_dir}/${propertiesChannelName}")
+  channelGradleProperties = string("${channelDir}/channel_gradle.properties")
+  overrideProperties(channelGradleProperties, false)
+  
+  ////  
+  // Import releaseProps from the RELEASE file
+  // or a file specified via JALVIEW_RELEASE_FILE if defined
+  // Expect jalview.version and target release branch in jalview.release        
+  def releaseProps = new Properties();
+  def releasePropFile = findProperty("JALVIEW_RELEASE_FILE");
+  def defaultReleasePropFile = "${jalviewDirAbsolutePath}/RELEASE";
+  try {
+    (new File(releasePropFile!=null ? releasePropFile : defaultReleasePropFile)).withInputStream { 
+     releaseProps.load(it)
+    }
+  } catch (Exception fileLoadError) {
+    throw new Error("Couldn't load release properties file "+(releasePropFile==null ? defaultReleasePropFile : "from custom location: releasePropFile"),fileLoadError);
+  }
+  ////
+  // Set JALVIEW_VERSION if it is not already set
+  if (findProperty("JALVIEW_VERSION")==null || "".equals(JALVIEW_VERSION)) {
+    JALVIEW_VERSION = releaseProps.get("jalview.version")
+  }
+
+  // essentials
+  bareSourceDir = string(source_dir)
+  sourceDir = string("${jalviewDir}/${bareSourceDir}")
+  resourceDir = string("${jalviewDir}/${resource_dir}")
+  bareTestSourceDir = string(test_source_dir)
+  testDir = string("${jalviewDir}/${bareTestSourceDir}")
+
+  classesDir = string("${jalviewDir}/${classes_dir}")
+
+  useClover = false
+
+  resourceClassesDir = classesDir
+
+  testSourceDir = testDir
+  testClassesDir = "${jalviewDir}/${test_output_dir}"
+
+  buildProperties = string("${classesDir}/${build_properties_file}")
+  getdownSetAppBaseProperty = false // whether to pass the appbase and appdistdir to the application
+
+  install4jApplicationName = "${jalview_name}"
+  
+  println("Using a ${CHANNEL} profile.")
+
+  additional_compiler_args = []
+  // configure classpath/args for j8/j11 compilation
+  if (JAVA_VERSION.equals("1.8")) {
+    JAVA_INTEGER_VERSION = string("8")
+    //libDir = j8libDir
+    libDir = j11libDir
+    libDistDir = j8libDir
+    compile_source_compatibility = 1.8
+    compile_target_compatibility = 1.8
+  } else if (JAVA_VERSION.equals("11")) {
+    JAVA_INTEGER_VERSION = string("11")
+    libDir = j11libDir
+    libDistDir = j11libDir
+    compile_source_compatibility = 11
+    compile_target_compatibility = 11
+  } else {
+    throw new GradleException("JAVA_VERSION=${JAVA_VERSION} not currently supported by Jalview")
+  }
+
+  resourceBuildDir = string("${buildDir}/resources")
+  resourcesBuildDir = string("${resourceBuildDir}/resources_build")
+  helpBuildDir = string("${resourceBuildDir}/help_build")
+  docBuildDir = string("${resourceBuildDir}/doc_build")
+
+  if (buildProperties == null) {
+    buildProperties = string("${resourcesBuildDir}/${build_properties_file}")
+  }
+  buildingHTML = string("${jalviewDir}/${doc_dir}/building.html")
+  helpParentDir = string("${jalviewDir}/${help_parent_dir}")
+  helpSourceDir = string("${helpParentDir}/${help_dir}")
+  helpFile = string("${helpBuildDir}/${help_dir}/help.jhm")
+
+  // ENDEXT
+}
+
+
+sourceSets {
+  main {
+    java {
+      srcDirs sourceDir
+      outputDir = file(classesDir)
+    }
+
+    resources {
+      srcDirs = [ resourcesBuildDir, docBuildDir, helpBuildDir ]
+    }
+
+    compileClasspath = files(sourceSets.main.java.outputDir)
+    compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
+
+
+    compileClasspath = files(sourceSets.main.java.outputDir)
+    compileClasspath += fileTree(dir: "${jalviewDir}/${libDir}", include: ["*.jar"])
+
+    runtimeClasspath = compileClasspath
+    runtimeClasspath += files(sourceSets.main.resources.srcDirs)
+  }
+
+  test {
+    java {
+      srcDirs testSourceDir
+      outputDir = file(testClassesDir)
+    }
+
+    resources {
+      srcDirs = useClover ? sourceSets.clover.resources.srcDirs : sourceSets.main.resources.srcDirs
+    }
+
+    compileClasspath = files( sourceSets.test.java.outputDir )
+    compileClasspath += useClover ? sourceSets.clover.compileClasspath : sourceSets.main.compileClasspath
+    compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**/*.jar"])
+
+    runtimeClasspath = compileClasspath
+    runtimeClasspath += files(sourceSets.test.resources.srcDirs)
+  }
+ /*  test {
+    java {
+      srcDirs testSourceDir
+      outputDir = file(testClassesDir)
+    }
+
+    resources {
+      srcDirs = sourceSets.main.resources.srcDirs
+    }
+
+    compileClasspath = files( sourceSets.test.java.outputDir )
+    compileClasspath += sourceSets.main.compileClasspath
+    compileClasspath += fileTree(dir: "${jalviewDir}/${utils_dir}/testnglibs", include: ["**   REMOVE_THIS_GAP  /*.jar"])
+
+    runtimeClasspath = compileClasspath
+  }
+*/
+}
+
+
+compileJava {
+  sourceCompatibility = compile_source_compatibility
+  targetCompatibility = compile_target_compatibility
+  options.compilerArgs = additional_compiler_args
+  doFirst {
+    print ("Setting target compatibility to "+compile_target_compatibility+"\n")
+  }
+}
+
+
+compileTestJava {
+  doFirst {
+    sourceCompatibility = compile_source_compatibility
+    targetCompatibility = compile_target_compatibility
+    options.compilerArgs = additional_compiler_args
+    print ("Setting target compatibility to "+targetCompatibility+"\n")
+  }
+}
+
+
+clean {
+  doFirst {
+    delete sourceSets.main.java.outputDir
+  }
+}
+
+
+cleanTest {
+  doFirst {
+    delete sourceSets.test.java.outputDir
+  }
+}
+
+
+// format is a string like date.format("dd MMMM yyyy")
+def getDate(format) {
+  def date = new Date()
+  return date.format(format)
+}
+
+
+task copyDocs(type: Copy) {
+  def inputDir = "${jalviewDir}/${doc_dir}"
+  def outputDir = "${docBuildDir}/${doc_dir}"
+  from(inputDir) {
+    include('**/*.txt')
+    include('**/*.md')
+    include('**/*.html')
+    include('**/*.xml')
+    filter(ReplaceTokens,
+      beginToken: '$$',
+      endToken: '$$',
+      tokens: [
+        'Version-Rel': JALVIEW_VERSION,
+        'Year-Rel': getDate("yyyy")
+      ]
+    )
+  }
+  from(inputDir) {
+    exclude('**/*.txt')
+    exclude('**/*.md')
+    exclude('**/*.html')
+    exclude('**/*.xml')
+  }
+  into outputDir
+
+  inputs.dir(inputDir)
+  outputs.dir(outputDir)
+}
+
+
+task copyHelp(type: Copy) {
+  def inputDir = helpSourceDir
+  def outputDir = "${helpBuildDir}/${help_dir}"
+  from(inputDir) {
+    include('**/*.txt')
+    include('**/*.md')
+    include('**/*.html')
+    include('**/*.hs')
+    include('**/*.xml')
+    include('**/*.jhm')
+    filter(ReplaceTokens,
+      beginToken: '$$',
+      endToken: '$$',
+      tokens: [
+        'Version-Rel': JALVIEW_VERSION,
+        'Year-Rel': getDate("yyyy")
+      ]
+    )
+  }
+  from(inputDir) {
+    exclude('**/*.txt')
+    exclude('**/*.md')
+    exclude('**/*.html')
+    exclude('**/*.hs')
+    exclude('**/*.xml')
+    exclude('**/*.jhm')
+  }
+  into outputDir
+
+  inputs.dir(inputDir)
+  outputs.files(helpFile)
+  outputs.dir(outputDir)
+}
+
+
+task copyResources(type: Copy) {
+  group = "build"
+  description = "Copy (and make text substitutions in) the resources dir to the build area"
+
+  def inputDir = resourceDir
+  def outputDir = resourcesBuildDir
+  from(inputDir) {
+    include('**/*.txt')
+    include('**/*.md')
+    include('**/*.html')
+    include('**/*.xml')
+    filter(ReplaceTokens,
+      beginToken: '$$',
+      endToken: '$$',
+      tokens: [
+        'Version-Rel': JALVIEW_VERSION,
+        'Year-Rel': getDate("yyyy")
+      ]
+    )
+  }
+  from(inputDir) {
+    exclude('**/*.txt')
+    exclude('**/*.md')
+    exclude('**/*.html')
+    exclude('**/*.xml')
+  }
+  into outputDir
+
+  inputs.dir(inputDir)
+  outputs.dir(outputDir)
+}
+
+task copyChannelResources(type: Copy) {
+  dependsOn copyResources
+  group = "build"
+  description = "Copy the channel resources dir to the build resources area"
+
+  def inputDir = "${channelDir}/${resource_dir}"
+  def outputDir = resourcesBuildDir
+  from inputDir
+  into outputDir
+
+  inputs.dir(inputDir)
+  outputs.dir(outputDir)
+}
+
+task createBuildProperties(type: Copy) {
+  // using the build_properties already included in the source tarball
+  def inputFile = "build_properties"
+  def outputFile = buildProperties
+  from inputFile
+  into file(outputFile).getParent()
+  rename(file(inputFile).getName(), file(outputFile).getName())
+
+  inputs.file(inputFile)
+  outputs.file(outputFile)
+}
+
+
+task buildIndices(type: JavaExec) {
+  dependsOn copyHelp
+  classpath = sourceSets.main.compileClasspath
+  main = "com.sun.java.help.search.Indexer"
+  workingDir = "${helpBuildDir}/${help_dir}"
+  def argDir = "html"
+  args = [ argDir ]
+  inputs.dir("${workingDir}/${argDir}")
+
+  outputs.dir("${classesDir}/doc")
+  outputs.dir("${classesDir}/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 buildResources {
+  dependsOn copyResources
+  dependsOn copyChannelResources
+  dependsOn createBuildProperties
+}
+
+task prepare {
+  dependsOn buildResources
+  dependsOn copyDocs
+  dependsOn copyHelp
+  dependsOn buildIndices
+}
+
+
+compileJava.dependsOn prepare
+run.dependsOn compileJava
+//run.dependsOn prepare
+
+
+//testReportDirName = "test-reports" // note that test workingDir will be $jalviewDir
+test {
+  dependsOn prepare
+  dependsOn compileJava //?
+
+  useTestNG() {
+    includeGroups testng_groups
+    excludeGroups testng_excluded_groups
+    preserveOrder true
+    useDefaultListeners=true
+  }
+
+  maxHeapSize = "1024m"
+
+  workingDir = jalviewDir
+  //systemProperties 'clover.jar' System.properties.clover.jar
+  def testLaf = project.findProperty("test_laf")
+  if (testLaf != null) {
+    println("Setting Test LaF to '${testLaf}'")
+    systemProperty "laf", testLaf
+  }
+  def testHiDPIScale = project.findProperty("test_HiDPIScale")
+  if (testHiDPIScale != null) {
+    println("Setting Test HiDPI Scale to '${testHiDPIScale}'")
+    systemProperty "sun.java2d.uiScale", testHiDPIScale
+  }
+  sourceCompatibility = compile_source_compatibility
+  targetCompatibility = compile_target_compatibility
+  jvmArgs += additional_compiler_args
+
+  doFirst {
+  }
+}
+
+
+task compileLinkCheck(type: JavaCompile) {
+  options.fork = true
+  classpath = files("${jalviewDir}/${utils_dir}")
+  destinationDir = file("${jalviewDir}/${utils_dir}")
+  source = fileTree(dir: "${jalviewDir}/${utils_dir}", include: ["HelpLinksChecker.java", "BufferedLineReader.java"])
+
+  inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
+  inputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.java")
+  outputs.file("${jalviewDir}/${utils_dir}/HelpLinksChecker.class")
+  outputs.file("${jalviewDir}/${utils_dir}/BufferedLineReader.class")
+}
+
+
+task linkCheck(type: JavaExec) {
+  dependsOn prepare
+  dependsOn compileLinkCheck
+
+  def helpLinksCheckerOutFile = file("${jalviewDir}/${utils_dir}/HelpLinksChecker.out")
+  classpath = files("${jalviewDir}/${utils_dir}")
+  main = "HelpLinksChecker"
+  workingDir = jalviewDir
+  args = [ "${helpBuildDir}/${help_dir}", "-nointernet" ]
+
+  def outFOS = new FileOutputStream(helpLinksCheckerOutFile, false) // false == don't append
+  standardOutput = new org.apache.tools.ant.util.TeeOutputStream(
+    outFOS,
+    System.out)
+  errorOutput = new org.apache.tools.ant.util.TeeOutputStream(
+    outFOS,
+    System.err)
+
+  inputs.dir(helpBuildDir)
+  outputs.file(helpLinksCheckerOutFile)
+}
+
+
+// import the pubhtmlhelp target
+ant.properties.basedir = "${jalviewDir}"
+ant.properties.helpBuildDir = "${helpBuildDir}/${help_dir}"
+ant.importBuild "${utils_dir}/publishHelp.xml"
+
+
+task cleanPackageDir(type: Delete) {
+  doFirst {
+    delete fileTree(dir: "${jalviewDir}/${package_dir}", include: "*.jar")
+  }
+}
+
+
+jar {
+  dependsOn prepare
+  dependsOn linkCheck
+
+  manifest {
+    attributes "Main-Class": main_class,
+    "Permissions": "all-permissions",
+    "Application-Name": install4jApplicationName,
+    "Codebase": application_codebase,
+    "Implementation-Version": JALVIEW_VERSION
+  }
+
+  def outputDir = "${jalviewDir}/${package_dir}"
+  destinationDirectory = file(outputDir)
+  archiveFileName = rootProject.name+".jar"
+
+  exclude "cache*/**"
+  exclude "*.jar"
+  exclude "*.jar.*"
+  exclude "**/*.jar"
+  exclude "**/*.jar.*"
+
+  inputs.dir(sourceSets.main.java.outputDir)
+  sourceSets.main.resources.srcDirs.each{ dir ->
+    inputs.dir(dir)
+  }
+
+  outputs.file("${outputDir}/${archiveFileName}")
+}
+
diff --git a/utils/debian/etc/jalview_properties b/utils/debian/etc/jalview_properties
new file mode 100644 (file)
index 0000000..0275ae3
--- /dev/null
@@ -0,0 +1,60 @@
+# global template for ~/.jalview_properties for debian systems
+# 
+# this file is copied into a user's home directory by 
+# the jalview wrapper script when no .jalview_properties file
+# exists. 
+#
+# changes to this file will not affect the jalview configuration
+# for existing users (at least not at the time of writing)
+
+# Configure the automatic display of the Jalview Example Project for new users
+# default is true
+# SHOW_STARTUP_FILE=false
+
+# Location of the file to be loaded in on startup if SHOW_STARTUP_FILE is true
+# default is https://www.jalview.org/examples/exampleFile_2_7.jvp
+# STARTUP_FILE=https://www.jalview.org/examples/exampleFile_2_7.jvp
+
+# 
+# Disable Jalview's connections to various web services
+#
+
+# Connection: https://www.jalview.org/services/identifiers 
+# uncomment to stop jalview retrieving a list of URL templates for
+# biological databases originally provided by identifiers.org
+#
+# NOIDENTIFIERSSERVICE=true
+
+# Connection: https://www.jalview.org/feeds/desktop/rss
+# uncomment to stop Jalview checking the www.jalview.org news feed
+# NONEWS=true
+
+# Connection: https://raw.githubusercontent.com/jalview/exporter-templates/master/biojs/package.json
+# uncomment to disable download of the latest 'BioJS' HTML export template
+# NOHTMLTEMPLATES=true
+
+# Uncomment to disable pings to google analytics 
+# these provide record of launch statistics for the Jalview project
+#
+# default is to prompt user on launch. 
+# Allowed values are 'true', 'false' or undefined
+# USAGESTATS=false
+
+# Uncomment to disable jalview's retrieval of https://www.jalview.org/cgi-bin/questionnaire.pl
+# this service is used to make Jalview users aware 
+# of any community questionnaires currently active on www.jalview.org 
+# Allowed values are true/false. Default is true.
+# NOQUESTIONNAIRES=true
+
+# Uncomment to disable jalview's retrieval of https://www.jalview.org/webstart/jalview.jnlp
+# this document contains the version number of the latest release
+# and used to notify the user if they should consider upgrading their installation.
+# Allowed values are true/false. Default is true.
+# VERSION_CHECK=false
+
+# Uncomment to prevent jalview automatically contacting
+# https://www.compbio.dundee.ac.uk/jabaws/ to discover web services
+# When disabled, users can still initiate service discovery via a button 
+# in the 'Web Services' menu.
+# Allowed values are true or false. Default is true.
+# SHOW_JWS2_SERVICES=false 
\ No newline at end of file
diff --git a/utils/debian/file_associations_template-mailcap.txt b/utils/debian/file_associations_template-mailcap.txt
new file mode 100644 (file)
index 0000000..9a96e97
--- /dev/null
@@ -0,0 +1 @@
+$$MIMETYPE$$; jalview -open '%s'; description="$$NAME$$ File"; nametemplate=%s.$$EXTENSION$$; test=test -n "$DISPLAY"; priority=$$PRIORITY$$
diff --git a/utils/debian/file_associations_template-shared-mime-info.xml b/utils/debian/file_associations_template-shared-mime-info.xml
new file mode 100644 (file)
index 0000000..003423c
--- /dev/null
@@ -0,0 +1,6 @@
+  <mime-type type="$$MIMETYPE$$">
+    <comment>$$NAME$$ File</comment>
+    <generic-icon name="$$ICONFILE$$"/>
+$$    <glob pattern="*.EXTENSIONS" weight="$$PRIORITY$$0"/>
+$$  </mime-type>
+
diff --git a/utils/debian/mime_types_for_debian.pl b/utils/debian/mime_types_for_debian.pl
new file mode 100755 (executable)
index 0000000..15e1ea1
--- /dev/null
@@ -0,0 +1,249 @@
+#!/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-mmcif",
+  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"]},
+  mmcif => {shortname=>"mmcif",name=>"CIF",extensions=>["cif"]},
+  mmcif2 => {shortname=>"mmcif2",name=>"mmCIF",extensions=>["mcif","mmcif"]},
+  #jvl => {shortname=>"jvl",name=>"Jalview Launch",extensions=>["jvl"],iconfile=>"jalview-launch"},
+  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 @non_primary = qw(mmcif mmcif2 pdb);
+
+my $mailcaptemplatefile = "file_associations_template-mailcap.txt";
+my $mailcaptemplate;
+my $sharedmimeinfotemplatefile = "file_associations_template-shared-mime-info.xml";
+my $sharedmimeinfotemplate;
+
+open(SMT,"<$sharedmimeinfotemplatefile") or die("Could not open '$sharedmimeinfotemplatefile' for reading");
+while(<SMT>){
+  $sharedmimeinfotemplate .= $_;
+}
+close(SMT);
+open(MCT,"<$mailcaptemplatefile") or die("Could not open '$mailcaptemplatefile' for reading");
+while(<MCT>){
+  $mailcaptemplate .= $_;
+}
+close(MCT);
+my $sharedmimeinfoauto;
+my $mailcapauto;
+
+# this file should go in /usr/share/mime/packages
+my $sharedmimeinfoautofile = "debian/jalview-mime.xml";
+
+# this file should go in /usr/lib/mime/packages
+my $mailcapautofile = "debian/jalview-mailcap";
+
+# this should be part of the jalview.desktop file that goes in /usr/shares/applications
+my $desktopfile = "debian/jalview.desktop";
+
+my $MimeType = "";
+
+for my $key (sort keys %$add_associations) {
+  my $a = $add_associations->{$key};
+  warn("Known file association for $a->{shortname} (".join(",",@{$a->{extensions}}).")\n");
+}
+
+open(SMI,">$sharedmimeinfoautofile") or die ("Could not open '$sharedmimeinfoautofile' for writing");
+
+open(MCA,">$mailcapautofile") or die ("Could not open '$mailcapautofile' 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 = map(lc($_),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("Reading file association for $shortname (".join(",",@extensions).")\n");
+  }
+}
+close(IN);
+
+my %all_associations = (%$file_associations, %$add_associations);
+
+my @ordered = (@put_first, @non_primary);
+for my $key (sort keys %all_associations) {
+  next if grep($_ eq $key, @ordered);
+  push(@ordered, $key);
+}
+my $num = $#ordered + 1;
+
+warn("--\n");
+print SMI qq(<?xml version="1.0" encoding="UTF-8"?>\n<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">\n\n);
+
+my $mailcapcount = 0;
+for my $shortname (@ordered) {
+  my $a = $all_associations{$shortname};
+  next if (ref($a) ne "HASH");
+
+  my $name = $a->{name};
+  my $extensions = $a->{extensions};
+  my $mimetype = $mimetypes->{$shortname};
+  $mimetype = "application/x-$shortname+txt" unless $mimetype;
+
+  $MimeType .= $MimeType?";":"";
+  $MimeType .= $mimetype;
+
+  my $iconfile = $a->{iconfile};
+  $iconfile = "x-jalview-file" unless $iconfile;
+
+  my $primary = (! grep($_ eq $shortname, @non_primary));
+  my $primarystring = $primary?"true":"false";
+  my $role = $primary?"Editor":"Viewer";
+  my $priority = $primary?9:4;
+  $priority = 10 if $mimetype =~ m/\bjalview\b/;
+
+  my @extensions = @$extensions;
+  my $extension0 = $extensions[0];
+
+  my $xname = xml_escape($name);
+  my $xmimetype = xml_escape($mimetype);
+  my $xshortname = xml_escape($shortname);
+  my $xiconfile = xml_escape($iconfile);
+  my $xrole = xml_escape($role);
+  my $xROLE = xml_escape(uc($role));
+  my $xprimarystring = xml_escape($primarystring);
+
+  my $sharedmimeinfoentry = $sharedmimeinfotemplate;
+  $sharedmimeinfoentry =~ s/\$\$NAME\$\$/$xname/g;
+  $sharedmimeinfoentry =~ s/\$\$SHORTNAME\$\$/$xshortname/g;
+  $sharedmimeinfoentry =~ s/\$\$MIMETYPE\$\$/$xmimetype/g;
+  $sharedmimeinfoentry =~ s/\$\$ICONFILE\$\$/$xiconfile/g;
+  $sharedmimeinfoentry =~ s/\$\$ROLE\$\$/$xrole/g;
+  $sharedmimeinfoentry =~ s/\$\$PRIMARY\$\$/$xprimarystring/g;
+  $sharedmimeinfoentry =~ s/\$\$PRIORITY\$\$/$priority/g;
+  while ($sharedmimeinfoentry =~ m/\$\$([^\$]*)EXTENSIONS([^\$]*)\$\$/) {
+    my $pre = $1;
+    my $post = $2;
+    my $sharedmimeinfoextensions;
+    for my $ext (@extensions) {
+      my $xext = xml_escape($ext);
+      $sharedmimeinfoextensions .= $pre.$xext.$post;
+    }
+    my $prere = $pre;
+    $prere =~ s/([\*\.])/\\\1/g;
+    my $postre = $post;
+    $postre =~ s/([\*\.])/\\\1/g;
+    $sharedmimeinfoentry =~ s/\$\$${prere}EXTENSIONS${postre}\$\$/$sharedmimeinfoextensions/gs;
+  }
+  print SMI $sharedmimeinfoentry;
+
+  my $mailcapentry = $mailcaptemplate;
+  $mailcapentry =~ s/\$\$NAME\$\$/$xname/g;
+  $mailcapentry =~ s/\$\$SHORTNAME\$\$/$xshortname/g;
+  $mailcapentry =~ s/\$\$MIMETYPE\$\$/$xmimetype/g;
+  $mailcapentry =~ s/\$\$ICONFILE\$\$/$xiconfile/g;
+  $mailcapentry =~ s/\$\$PRIMARY\$\$/$xprimarystring/g;
+  $mailcapentry =~ s/\$\$MACASSOCIATIONROLE\$\$/$xROLE/g;
+  $mailcapentry =~ s/\$\$EXTENSION\$\$/$extension0/g;
+  $mailcapentry =~ s/\$\$PRIORITY\$\$/$priority/g;
+
+  my $ext = join(",",sort(@extensions));
+  my $xdisplayext = xml_escape(join(", ", map(".$_",sort(@extensions))));
+  my $progresspercent = int(($mailcapcount/$num)*100);
+  $progresspercent = 100 if $progresspercent > 100;
+  $mailcapcount++;
+  my $xext = xml_escape($ext);
+  my $addunixextension = "true";
+
+  $mailcapentry =~ s/\$\$ADDUNIXEXTENSION\$\$/$addunixextension/g;
+  $mailcapentry =~ s/\$\$EXTENSION\$\$/$xext/g;
+  $mailcapentry =~ s/\$\$DISPLAYEXTENSION\$\$/$xdisplayext/g;
+  $mailcapentry =~ s/\$\$PROGRESSPERCENT\$\$/$progresspercent/g;
+  $mailcapentry =~ s/\$\$ID\$\$/$id/g;
+  $id++;
+  $mailcapentry =~ s/\$\$ID1\$\$/$id/g;
+  $id++;
+  $mailcapentry =~ s/\$\$ID2\$\$/$id/g;
+  $id++;
+
+  print MCA $mailcapentry;
+
+  delete $all_associations{$shortname};
+  warn("Writing entry for $name (".join(",",@$extensions).": $mimetype)\n");
+}
+
+print SMI "</mime-info>\n";
+
+close(MCA);
+close(SMI);
+
+open(D,">$desktopfile") or die ("Could not open '$desktopfile' for writing");
+print D qq([Desktop Entry]
+Version=1.1
+Type=Application
+Name=Jalview
+Comment=Multiple Sequence Alignment Editor
+Icon=jalview-icon
+Type=Application
+TryExec=jalview
+Exec=jalview %u
+Terminal=false
+Categories=Science;Biology;
+Keywords=alignment;sequence;
+MimeType=${MimeType}
+);
+close(D);
+
+sub xml_escape {
+  my $x = shift;
+  # stolen from Pod::Simple::XMLOutStream in base distro
+  $x =~ s/([^-\n\t !\#\$\%\(\)\*\+,\.\~\/\:\;=\?\@\[\\\]\^_\`\{\|\}a-zA-Z0-9])/'&#'.(ord($1)).';'/eg;
+  return $x;
+}  
diff --git a/utils/getdown/bin/jalview.bat b/utils/getdown/bin/jalview.bat
new file mode 100755 (executable)
index 0000000..0eca4e4
--- /dev/null
@@ -0,0 +1,50 @@
+@ECHO OFF
+
+REM This is the Jalview batch script wrapper to run the powershell script of the same name.
+REM There is nothing specific to Jalview.
+
+REM ******************************************************************************
+REM If you need to set a full path to the PowerShell executable please do so here:
+SET PWSHPATH=
+REM ******************************************************************************
+
+REM This is some DOS magic to substitute the extension in the full path of this batch script with .ps1
+SET SCRIPTPATH=%~dpn0.ps1
+
+REM PowerShell script isn't where it should be!
+IF NOT EXIST %SCRIPTPATH% (
+  ECHO Could not find PowerShell script %SCRIPTPATH%.  Is %~nx0 in the right folder?
+  EXIT /B 1
+)
+
+REM Look for either pwsh.exe or powershell.exe if not set in PWSHPATH above.
+REM pwsh.exe is preferred as it is likely to be a newer version.
+SET PWSH=
+IF DEFINED PWSHPATH (
+  SET PWSH=%PWSHPATH%
+)
+FOR %%X IN (pwsh.exe powershell.exe) DO (
+  IF NOT DEFINED PWSH ( 
+    IF NOT "%%~$PATH:X" == "" (
+      REM Found a PowerShell executable in the PATH
+      SET PWSH=%%X
+      GOTO end_looking
+    )
+  )
+)
+:end_looking
+
+IF NOT DEFINED PWSH (
+  REM No PowerShell executable found -- tell the user what to do.
+  ECHO No PowerShell found in %%PATH%%. If PowerShell is installed either
+  ECHO 1. add it to your PATH, or
+  ECHO 2. edit the PWSHPATH value at the top of this file:
+  ECHO    "%~dpnx0"
+  ECHO.
+  ECHO %~n0 on the command line requires PowerShell. To install PowerShell see
+  ECHO https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell
+  EXIT /B 2
+)
+
+REM Run the PowerShell script
+%PWSH% -NoProfile -ExecutionPolicy Bypass -Command "& '%SCRIPTPATH%' %*";
diff --git a/utils/getdown/bin/jalview.ps1 b/utils/getdown/bin/jalview.ps1
new file mode 100755 (executable)
index 0000000..7ae9a57
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/env pwsh
+
+# save args and first parameter
+$myArgs = $args.Clone()
+$myArg1 = $args[0]
+
+# setup for powershell version < 6.0
+[bool] $myIsWindows = 0
+[bool] $myIsMacOS = 0
+if ( $IsWindows -eq $null ) {
+  # for powershell version < 6.0 let's assume Windows
+  $myIsWindows = 1
+  $myIsMacOS = 0
+} else {
+  $myIsWindows = $IsWindows
+  $myIsMacOS = $IsMacOS
+}
+
+# parent dir of this actual script (which should be the getdown appdir/bin). Follow all symlinks.  Like GNU readlink -f
+function Readlink-f {
+  Param($Link)
+  $Return = $null
+  $c = 0
+  $max = 100 # just in case we end up in a loop
+  [bool] $found = 0
+  $file = Get-Item -Path $Link
+  $prevfile = $null
+  While ( $c -lt $max -and "${file}" -ne "${prevfile}" -and -not $found ) {
+    $prevfile = $file
+    [string] $target = ( $file ).Target
+    If ( $target -eq $null -or ( $file ).LinkType -ne "SymbolicLink" ) {
+      $Return = $file
+      $found = 1
+    } Else {
+      If ( $( Split-Path -Path $target -IsAbsolute ) ) {
+        $file = Get-Item -Path $target
+      } Else {
+# symbolic link is relative: combine previous link parent dir with the link target and resolve
+        $file = Get-Item -Path ( Join-Path -Path ( Split-Path -Path $prevfile -Parent ) -ChildPath $target -Resolve )
+      }
+    }
+    $c++
+  }
+  if ( -not $found ) {
+    throw "Could not determine path to actual file $( Split-Path -Path $Link -Leaf )"
+  }
+  $Return
+}
+
+# Avert problem with unix version of powershell and tell user the reason (Windows must always have .ps1 extension)
+if ( $MyInvocation.MyCommand.Path -eq $null ) {
+  throw "Script or link to script must have extension .ps1"
+}
+
+
+$CMDPATH = ( Get-Item $MyInvocation.MyCommand.Path )
+$SCRIPTPATH = Readlink-f -Link $CMDPATH
+$DIR = Split-Path -Path $SCRIPTPATH -Parent
+
+# set the "-open" parameter if myArg1 is non-zero-length, and not "open" or starts with a "-"
+$OPEN = ""
+if ( $myArg1.length -gt 0 -and ( -not $myArg1.StartsWith("-") ) -and $myArg1 -cne "open" ) {
+  $OPEN = "-open"
+}
+
+$APPDIR = If ( ( Split-Path -Path $DIR -Leaf ) -eq "bin" ) { Split-Path -Path $DIR -Parent } Else { $DIR }
+$JAVAEXE = If ( $myIsWindows ) { "java.exe" } Else { "java" }
+$JAVA = Join-Path -Path $APPDIR -ChildPath ( "jre/" + $( If ( $myIsMacOS ) { "Contents/Home/" } Else { "" } ) + "bin/${JAVAEXE}" )
+$GETDOWNTXT = Join-Path -Path $APPDIR -ChildPath "getdown.txt"
+
+# look for getdown.txt -- needed to create classpath
+if ( -not ( Test-Path -Path "${GETDOWNTXT}" ) ) {
+  throw "Cannot find ${GETDOWNTXT}"
+}
+
+# look for bundled JRE. Might not be there if unix installer used in which case just invoke "java"
+if ( -not ( Test-Path -Path "${JAVA}" ) ) {
+  Write-Host "Cannot find bundled ${JAVAEXE}. Using system ${JAVAEXE} and hoping for the best!"
+  $JAVA = $JAVAEXE
+}
+
+$CLASSPATH = ( Select-String -Path "${GETDOWNTXT}" -AllMatches -Pattern "code\s*=\s*(.*)$" | foreach { Join-Path -Path $APPDIR -ChildPath $($_.Matches.Groups[1].Value ) } ) -join $( If ( $myIsWindows ) { ";" } Else { ":" } )
+
+# quote the args and the command (in case of spaces) with escape chars (`) and precede with & to indicate command not string
+$myArgsString = '"' + $($myArgs -join '" "') + '"'
+Invoke-Expression -Command "& `"${JAVA}`" -cp `"${CLASSPATH}`" jalview.bin.Launcher ${OPEN} ${myArgsString}"
+
diff --git a/utils/getdown/bin/jalview.sh b/utils/getdown/bin/jalview.sh
new file mode 100755 (executable)
index 0000000..112c3e6
--- /dev/null
@@ -0,0 +1,132 @@
+#!/usr/bin/env bash
+
+declare -a ARGS=("${@}")
+ARG1=$1
+
+# this whole next part is because there's no readlink -f in Darwin
+function readlinkf() {
+  FINDFILE="$1"
+  FILE="${FINDFILE}"
+  PREVFILE=""
+  C=0
+  MAX=100 # just in case we end up in a loop
+  FOUND=0
+  while [ "${C}" -lt "${MAX}" -a "${FILE}" != "${PREVFILE}" -a "${FOUND}" -ne 1 ]; do
+    PREVFILE="${FILE}"
+    FILE="$(readlink "${FILE}")"
+    if [ -z "${FILE}" ]; then
+      # the readlink is empty means we've arrived at the script, let's canonicalize with pwd
+      FILE="$(cd "$(dirname "${PREVFILE}")" &> /dev/null && pwd -P)"/"$(basename "${PREVFILE}")"
+      FOUND=1
+    elif [ "${FILE#/}" = "${FILE}" ]; then
+      # FILE is not an absolute path link, we need to add the relative path to the previous dir
+      FILE="$(dirname "${PREVFILE}")/${FILE}"
+    fi
+    C=$((C+1))
+  done
+  if [ "${FOUND}" -ne 1 ]; then
+    echo "Could not determine path to actual file '$(basename "${FINDFILE}")'" >&2
+    exit 1
+  fi
+  echo "${FILE}"
+}
+
+ISMACOS=0
+if [ "$( uname -s )" = "Darwin" ]; then
+  ISMACOS=1
+fi
+
+declare -a JVMARGS=()
+
+# set vars for being inside the macos App Bundle
+if [ "${ISMACOS}" = 1 ]; then
+# MACOS ONLY
+  DIR="$(dirname "$(readlinkf "$0")")"
+  APP="${DIR%.app/Contents/*}".app
+  if [ "${APP}" = "${APP%.app}" ]; then
+    echo "Could not find Jalview.app" >&2
+    exit 2
+  fi
+  APPDIR="${APP}/Contents/Resources/app"
+  JAVA="${APPDIR}/jre/Contents/Home/bin/java"
+  JVMARGS=( "${JVMARGS[@]}" "-Xdock:icon=${APPDIR}/resource/jalview_logo.png" )
+else
+# NOT MACOS
+  DIR="$(dirname "$(readlink -f "$0")")"
+  APPDIR="${DIR%/bin}"
+  JAVA="${APPDIR}/jre/bin/java"
+fi
+
+SYSJAVA=java
+GETDOWNTXT="${APPDIR}/getdown.txt"
+
+CLASSPATH=""
+# save an array of JAR paths in case we're in WSL (see later)
+declare -a JARPATHS=()
+if [ -e "${GETDOWNTXT}" ]; then
+  # always check grep and sed regexes on macos -- they're not the same
+  for JAR in $(grep -e '^code\s*=\s*' "${GETDOWNTXT}" | sed -e 's/^code\s*=\s*//;'); do
+    [ -n "${CLASSPATH}" ] && CLASSPATH="${CLASSPATH}:"
+    CLASSPATH="${CLASSPATH}${APPDIR}/${JAR}"
+    JARPATHS=( "${JARPATHS[@]}" "${APPDIR}/${JAR}" )
+  done
+else
+  echo "Cannot find getdown.txt" >&2
+  exit 3
+fi
+
+# WINDOWS ONLY (Cygwin or WSL)
+# change paths for Cygwin or Windows Subsystem for Linux (WSL)
+if [ "${ISMACOS}" != 1 ]; then # macos doesn't like uname -o, best to avoid
+  if [ "$(uname -o)" = "Cygwin" ]; then
+  # CYGWIN
+    echo "When using relative paths in args within Cygwin, please start with './' or '../'" >&2
+    CLASSPATH=$(cygpath -pw "${CLASSPATH}")
+    # now for some arg paths fun. only translating paths starting with './', '../', '/' or '~'
+    ARGS=()
+    for ARG in "${@}"; do
+      if [ "${ARG}" != "${ARG#@(/|./|../|~)}" ]; then
+        ARGS=( "${ARGS[@]}" "$(cygpath -aw "${ARG}")" )
+      else
+        ARGS=( "${ARGS[@]}" "${ARG}" )
+      fi
+    done
+  elif uname -r | grep Microsoft >/dev/null; then
+  # WSL
+    echo "When using relative paths in args within WSL, please start with './' or '../'" >&2
+    CLASSPATH=""
+    for JARPATH in "${JARPATHS[@]}"; do
+      [ -n "${CLASSPATH}" ] && CLASSPATH="${CLASSPATH};"
+      CLASSPATH="${CLASSPATH}$(wslpath -aw "${JARPATH}")"
+    done
+    ARGS=()
+    for ARG in "${@}"; do
+      if [ "${ARG}" != "${ARG#@(/|./|../|~)}" ]; then
+        # annoyingly wslpath does not work if the file doesn't exist!
+        ARGBASENAME="$(basename "${ARG}")"
+        ARGDIRNAME="$(dirname "${ARG}")"
+        ARGS=( "${ARGS[@]}" "$(wslpath -aw "${ARGDIRNAME}")\\${ARGBASENAME}" )
+      else
+        ARGS=( "${ARGS[@]}" "${ARG}" )
+      fi
+    done
+    JAVA="${JAVA}.exe"
+    SYSJAVA="java.exe"
+  fi
+fi
+
+# Is there a bundled Java?  If not just try one in the PATH (do need .exe in WSL)
+if [ \! -e "${JAVA}" ]; then
+  JAVA=$SYSJAVA
+  echo "Cannot find bundled java, using system ${JAVA} and hoping for the best!" >&2
+fi
+
+# check to see if $1 is set and is not start of other cli args
+OPEN=""
+if [ -n "${ARG1}" -a "${ARG1}" = "${ARG1#-}" -a "${ARG1}" != "open" ]; then
+ # first argument exists and does not start with a "-" and is not "open"
+ OPEN="-open"
+fi
+
+# don't quote $OPEN (don't want it accidentally mistaken as an empty string arg!)
+"${JAVA}" "${JVMARGS[@]}" -cp "${CLASSPATH}" jalview.bin.Launcher ${OPEN} "${ARGS[@]}"
diff --git a/utils/install4j/DS_Store b/utils/install4j/DS_Store
deleted file mode 100644 (file)
index cada68d..0000000
Binary files a/utils/install4j/DS_Store and /dev/null differ
diff --git a/utils/install4j/DS_Store-DEVELOP b/utils/install4j/DS_Store-DEVELOP
deleted file mode 100644 (file)
index a65165c..0000000
Binary files a/utils/install4j/DS_Store-DEVELOP and /dev/null differ
diff --git a/utils/install4j/DS_Store-NON-RELEASE b/utils/install4j/DS_Store-NON-RELEASE
deleted file mode 100644 (file)
index 0d67d11..0000000
Binary files a/utils/install4j/DS_Store-NON-RELEASE and /dev/null differ
diff --git a/utils/install4j/DS_Store-TEST-RELEASE b/utils/install4j/DS_Store-TEST-RELEASE
deleted file mode 100644 (file)
index 164c584..0000000
Binary files a/utils/install4j/DS_Store-TEST-RELEASE and /dev/null differ
diff --git a/utils/install4j/DS_Store_no_link b/utils/install4j/DS_Store_no_link
deleted file mode 100644 (file)
index afa9a35..0000000
Binary files a/utils/install4j/DS_Store_no_link and /dev/null differ
diff --git a/utils/install4j/Uninstall Old Jalview.app/Contents/Info.plist b/utils/install4j/Uninstall Old Jalview.app/Contents/Info.plist
deleted file mode 100644 (file)
index 649770d..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-<?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
deleted file mode 100755 (executable)
index 191894d..0000000
Binary files a/utils/install4j/Uninstall Old Jalview.app/Contents/MacOS/applet and /dev/null differ
diff --git a/utils/install4j/Uninstall Old Jalview.app/Contents/PkgInfo b/utils/install4j/Uninstall Old Jalview.app/Contents/PkgInfo
deleted file mode 100644 (file)
index 3253614..0000000
+++ /dev/null
@@ -1 +0,0 @@
-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
deleted file mode 100644 (file)
index 3147d89..0000000
Binary files a/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/Scripts/main.scpt and /dev/null 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
deleted file mode 100644 (file)
index 67a2cbd..0000000
Binary files a/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.icns and /dev/null 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
deleted file mode 100644 (file)
index 8e05928..0000000
Binary files a/utils/install4j/Uninstall Old Jalview.app/Contents/Resources/applet.rsrc and /dev/null 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
deleted file mode 100644 (file)
index 5f23fc5..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-{\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
deleted file mode 100644 (file)
index 04a1485..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-<?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
deleted file mode 100644 (file)
index 3ab3516..0000000
Binary files a/utils/install4j/Uninstall Old Jalview.scpt and /dev/null differ
index fac86a3..2196a6a 100644 (file)
 <dict>
 <key>CFBundleTypeExtensions</key>
 <array>
+<string>txt</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>ENA Flatfile File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-embl+txt</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
 <string>fa</string>
 <string>fasta</string>
 </array>
 <dict>
 <key>CFBundleTypeExtensions</key>
 <array>
+<string>gb</string>
+<string>gbk</string>
+</array>
+<key>CFBundleTypeName</key>
+<string>GenBank Flatfile File</string>
+<key>CFBundleTypeIconFile</key>
+<string>Jalview-File.icns</string>
+<key>CFBundleTypeRole</key>
+<string>Editor</string>
+<key>CFBundleTypeMIMETypes</key>
+<array>
+<string>application/x-genbank+txt</string>
+</array>
+<key>LSIsAppleDefaultForType</key>
+<true/>
+</dict>
+
+<dict>
+<key>CFBundleTypeExtensions</key>
+<array>
 <string>gff2</string>
 </array>
 <key>CFBundleTypeName</key>
index b14d32a..0d80644 100644 (file)
                     </serializedBean>
                   </action>
 
-                  <action name="mmCIF (.mcif, .mmcif) progress bar 13" id="10010" customizedId="mmCIF-mcif,mmcif-10010-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="mmCIF (.mcif, .mmcif) progress bar 12" id="10010" customizedId="mmCIF-mcif,mmcif-10010-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="13" />
+                      <property name="percentValue" type="int" value="12" />
                     </serializedBean>
                   </action>
 
                     </serializedBean>
                   </action>
 
-                  <action name="PDB (.ent, .pdb) progress bar 17" id="10013" customizedId="PDB-ent,pdb-10013-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PDB (.ent, .pdb) progress bar 16" id="10013" customizedId="PDB-ent,pdb-10013-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="17" />
+                      <property name="percentValue" type="int" value="16" />
                     </serializedBean>
                   </action>
 
                     </serializedBean>
                   </action>
 
-                  <action name="AMSA (.amsa) progress bar 21" id="10016" customizedId="AMSA-amsa-10016-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="AMSA (.amsa) progress bar 20" id="10016" customizedId="AMSA-amsa-10016-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="21" />
+                      <property name="percentValue" type="int" value="20" />
                     </serializedBean>
                   </action>
 
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Annotations (.annotations, .jvannotations) progress bar 26" id="10019" customizedId="Jalview Annotations-annotations,jvannotations-10019-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Annotations (.annotations, .jvannotations) progress bar 24" id="10019" customizedId="Jalview Annotations-annotations,jvannotations-10019-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="26" />
+                      <property name="percentValue" type="int" value="24" />
                     </serializedBean>
                   </action>
 
                     </serializedBean>
                   </action>
 
-                  <action name="BioJSON (.biojson) progress bar 30" id="10022" customizedId="BioJSON-biojson-10022-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="BioJSON (.biojson) progress bar 28" id="10022" customizedId="BioJSON-biojson-10022-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="30" />
+                      <property name="percentValue" type="int" value="28" />
                     </serializedBean>
                   </action>
 
                     </serializedBean>
                   </action>
 
-                  <action name="BLC (.blc) progress bar 34" id="10025" customizedId="BLC-blc-10025-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="BLC (.blc) progress bar 32" id="10025" customizedId="BLC-blc-10025-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="34" />
+                      <property name="percentValue" type="int" value="32" />
                     </serializedBean>
                   </action>
 
                     </serializedBean>
                   </action>
 
-                  <action name="Clustal (.aln) progress bar 39" id="10028" customizedId="Clustal-aln-10028-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Clustal (.aln) progress bar 36" id="10028" customizedId="Clustal-aln-10028-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="39" />
+                      <property name="percentValue" type="int" value="36" />
                     </serializedBean>
                   </action>
 
                   </action>
 <!-- END -->
 
+<!-- ENA Flatfile (.txt) BEGIN -->
+                  <action name="ENA Flatfile (.txt) message" id="10030" customizedId="ENA Flatfile-txt-10030-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="detailMessage" type="string">ENA Flatfile (.txt)</property>
+                      <property name="statusMessage" type="string">Creating file associations...</property>
+                      <property name="useDetail" type="boolean" value="true" />
+                      <property name="useStatus" type="boolean" value="true" />
+                    </serializedBean>
+                  </action>
+
+                  <action name="ENA Flatfile (.txt) progress bar 40" id="10031" customizedId="ENA Flatfile-txt-10031-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="percentValue" type="int" value="40" />
+                    </serializedBean>
+                  </action>
+
+                  <action name="ENA Flatfile (.txt) file association" id="10032" customizedId="ENA Flatfile-txt-10032-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .txt file association">
+                    <serializedBean>
+                      <property name="description" type="string">ENA Flatfile File</property>
+                      <property name="extension" type="string">txt</property>
+                      <property name="launcherId" type="string">737</property>
+                      <property name="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </property>
+                      <property name="macRole" type="enum" class="com.install4j.runtime.beans.actions.desktop.MacAssociationRole" value="EDITOR" />
+                      <property name="restartFinder" type="boolean" value="true" />
+                      <property name="selected" type="boolean" value="true" />
+                      <property name="unix" type="boolean" value="true" />
+                      <property name="unixIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.png</string>
+                        </object>
+                      </property>
+                      <property name="unixMimeType" type="string">application/x-embl+txt</property>
+                      <property name="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </property>
+                    </serializedBean>
+                  </action>
+<!-- END -->
+
 <!-- Fasta (.fa, .fasta) BEGIN -->
-                  <action name="Fasta (.fa, .fasta) message" id="10030" customizedId="Fasta-fa,fasta-10030-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Fasta (.fa, .fasta) message" id="10033" customizedId="Fasta-fa,fasta-10033-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Fasta (.fa, .fasta)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Fasta (.fa, .fasta) progress bar 43" id="10031" customizedId="Fasta-fa,fasta-10031-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Fasta (.fa, .fasta) progress bar 44" id="10034" customizedId="Fasta-fa,fasta-10034-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="43" />
+                      <property name="percentValue" type="int" value="44" />
                     </serializedBean>
                   </action>
 
-                  <action name="Fasta (.fa, .fasta) file association" id="10032" customizedId="Fasta-fa,fasta-10032-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .fa,fasta file association">
+                  <action name="Fasta (.fa, .fasta) file association" id="10035" customizedId="Fasta-fa,fasta-10035-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .fa,fasta file association">
                     <serializedBean>
                       <property name="description" type="string">Fasta File</property>
                       <property name="extension" type="string">fa,fasta</property>
 <!-- END -->
 
 <!-- Jalview Features (.features, .jvfeatures) BEGIN -->
-                  <action name="Jalview Features (.features, .jvfeatures) message" id="10033" customizedId="Jalview Features-features,jvfeatures-10033-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Features (.features, .jvfeatures) message" id="10036" customizedId="Jalview Features-features,jvfeatures-10036-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Jalview Features (.features, .jvfeatures)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Features (.features, .jvfeatures) progress bar 47" id="10034" customizedId="Jalview Features-features,jvfeatures-10034-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Jalview Features (.features, .jvfeatures) progress bar 48" id="10037" customizedId="Jalview Features-features,jvfeatures-10037-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="47" />
+                      <property name="percentValue" type="int" value="48" />
                     </serializedBean>
                   </action>
 
-                  <action name="Jalview Features (.features, .jvfeatures) file association" id="10035" customizedId="Jalview Features-features,jvfeatures-10035-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .features,jvfeatures file association">
+                  <action name="Jalview Features (.features, .jvfeatures) file association" id="10038" customizedId="Jalview Features-features,jvfeatures-10038-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .features,jvfeatures file association">
                     <serializedBean>
                       <property name="description" type="string">Jalview Features File</property>
                       <property name="extension" type="string">features,jvfeatures</property>
                   </action>
 <!-- END -->
 
+<!-- GenBank Flatfile (.gb, .gbk) BEGIN -->
+                  <action name="GenBank Flatfile (.gb, .gbk) message" id="10039" customizedId="GenBank Flatfile-gb,gbk-10039-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="detailMessage" type="string">GenBank Flatfile (.gb, .gbk)</property>
+                      <property name="statusMessage" type="string">Creating file associations...</property>
+                      <property name="useDetail" type="boolean" value="true" />
+                      <property name="useStatus" type="boolean" value="true" />
+                    </serializedBean>
+                  </action>
+
+                  <action name="GenBank Flatfile (.gb, .gbk) progress bar 52" id="10040" customizedId="GenBank Flatfile-gb,gbk-10040-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                    <serializedBean>
+                      <property name="percentValue" type="int" value="52" />
+                    </serializedBean>
+                  </action>
+
+                  <action name="GenBank Flatfile (.gb, .gbk) file association" id="10041" customizedId="GenBank Flatfile-gb,gbk-10041-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gb,gbk file association">
+                    <serializedBean>
+                      <property name="description" type="string">GenBank Flatfile File</property>
+                      <property name="extension" type="string">gb,gbk</property>
+                      <property name="launcherId" type="string">737</property>
+                      <property name="macIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.icns</string>
+                        </object>
+                      </property>
+                      <property name="macRole" type="enum" class="com.install4j.runtime.beans.actions.desktop.MacAssociationRole" value="EDITOR" />
+                      <property name="restartFinder" type="boolean" value="true" />
+                      <property name="selected" type="boolean" value="true" />
+                      <property name="unix" type="boolean" value="true" />
+                      <property name="unixIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.png</string>
+                        </object>
+                      </property>
+                      <property name="unixMimeType" type="string">application/x-genbank+txt</property>
+                      <property name="windowsIconFile">
+                        <object class="com.install4j.api.beans.ExternalFile">
+                          <string>Jalview-File.ico</string>
+                        </object>
+                      </property>
+                    </serializedBean>
+                  </action>
+<!-- END -->
+
 <!-- Generic Features Format v2 (.gff2) BEGIN -->
-                  <action name="Generic Features Format v2 (.gff2) message" id="10036" customizedId="Generic Features Format v2-gff2-10036-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Generic Features Format v2 (.gff2) message" id="10042" customizedId="Generic Features Format v2-gff2-10042-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Generic Features Format v2 (.gff2)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Generic Features Format v2 (.gff2) progress bar 52" id="10037" customizedId="Generic Features Format v2-gff2-10037-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Generic Features Format v2 (.gff2) progress bar 56" id="10043" customizedId="Generic Features Format v2-gff2-10043-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="52" />
+                      <property name="percentValue" type="int" value="56" />
                     </serializedBean>
                   </action>
 
-                  <action name="Generic Features Format v2 (.gff2) file association" id="10038" customizedId="Generic Features Format v2-gff2-10038-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gff2 file association">
+                  <action name="Generic Features Format v2 (.gff2) file association" id="10044" customizedId="Generic Features Format v2-gff2-10044-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gff2 file association">
                     <serializedBean>
                       <property name="description" type="string">Generic Features Format v2 File</property>
                       <property name="extension" type="string">gff2</property>
 <!-- END -->
 
 <!-- Generic Features Format v3 (.gff3) BEGIN -->
-                  <action name="Generic Features Format v3 (.gff3) message" id="10039" customizedId="Generic Features Format v3-gff3-10039-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Generic Features Format v3 (.gff3) message" id="10045" customizedId="Generic Features Format v3-gff3-10045-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Generic Features Format v3 (.gff3)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Generic Features Format v3 (.gff3) progress bar 56" id="10040" customizedId="Generic Features Format v3-gff3-10040-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Generic Features Format v3 (.gff3) progress bar 60" id="10046" customizedId="Generic Features Format v3-gff3-10046-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="56" />
+                      <property name="percentValue" type="int" value="60" />
                     </serializedBean>
                   </action>
 
-                  <action name="Generic Features Format v3 (.gff3) file association" id="10041" customizedId="Generic Features Format v3-gff3-10041-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gff3 file association">
+                  <action name="Generic Features Format v3 (.gff3) file association" id="10047" customizedId="Generic Features Format v3-gff3-10047-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .gff3 file association">
                     <serializedBean>
                       <property name="description" type="string">Generic Features Format v3 File</property>
                       <property name="extension" type="string">gff3</property>
 <!-- END -->
 
 <!-- JnetFile (.concise, .jnet) BEGIN -->
-                  <action name="JnetFile (.concise, .jnet) message" id="10042" customizedId="JnetFile-concise,jnet-10042-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="JnetFile (.concise, .jnet) message" id="10048" customizedId="JnetFile-concise,jnet-10048-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">JnetFile (.concise, .jnet)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="JnetFile (.concise, .jnet) progress bar 60" id="10043" customizedId="JnetFile-concise,jnet-10043-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="JnetFile (.concise, .jnet) progress bar 64" id="10049" customizedId="JnetFile-concise,jnet-10049-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="60" />
+                      <property name="percentValue" type="int" value="64" />
                     </serializedBean>
                   </action>
 
-                  <action name="JnetFile (.concise, .jnet) file association" id="10044" customizedId="JnetFile-concise,jnet-10044-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .concise,jnet file association">
+                  <action name="JnetFile (.concise, .jnet) file association" id="10050" customizedId="JnetFile-concise,jnet-10050-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .concise,jnet file association">
                     <serializedBean>
                       <property name="description" type="string">JnetFile File</property>
                       <property name="extension" type="string">concise,jnet</property>
 <!-- END -->
 
 <!-- MSF (.msf) BEGIN -->
-                  <action name="MSF (.msf) message" id="10045" customizedId="MSF-msf-10045-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="MSF (.msf) message" id="10051" customizedId="MSF-msf-10051-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">MSF (.msf)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="MSF (.msf) progress bar 65" id="10046" customizedId="MSF-msf-10046-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="MSF (.msf) progress bar 68" id="10052" customizedId="MSF-msf-10052-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="65" />
+                      <property name="percentValue" type="int" value="68" />
                     </serializedBean>
                   </action>
 
-                  <action name="MSF (.msf) file association" id="10047" customizedId="MSF-msf-10047-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .msf file association">
+                  <action name="MSF (.msf) file association" id="10053" customizedId="MSF-msf-10053-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .msf file association">
                     <serializedBean>
                       <property name="description" type="string">MSF File</property>
                       <property name="extension" type="string">msf</property>
 <!-- END -->
 
 <!-- PFAM (.pfam) BEGIN -->
-                  <action name="PFAM (.pfam) message" id="10048" customizedId="PFAM-pfam-10048-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PFAM (.pfam) message" id="10054" customizedId="PFAM-pfam-10054-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">PFAM (.pfam)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="PFAM (.pfam) progress bar 69" id="10049" customizedId="PFAM-pfam-10049-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PFAM (.pfam) progress bar 72" id="10055" customizedId="PFAM-pfam-10055-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="69" />
+                      <property name="percentValue" type="int" value="72" />
                     </serializedBean>
                   </action>
 
-                  <action name="PFAM (.pfam) file association" id="10050" customizedId="PFAM-pfam-10050-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pfam file association">
+                  <action name="PFAM (.pfam) file association" id="10056" customizedId="PFAM-pfam-10056-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pfam file association">
                     <serializedBean>
                       <property name="description" type="string">PFAM File</property>
                       <property name="extension" type="string">pfam</property>
 <!-- END -->
 
 <!-- PHYLIP (.phy) BEGIN -->
-                  <action name="PHYLIP (.phy) message" id="10051" customizedId="PHYLIP-phy-10051-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PHYLIP (.phy) message" id="10057" customizedId="PHYLIP-phy-10057-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">PHYLIP (.phy)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="PHYLIP (.phy) progress bar 73" id="10052" customizedId="PHYLIP-phy-10052-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PHYLIP (.phy) progress bar 76" id="10058" customizedId="PHYLIP-phy-10058-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="73" />
+                      <property name="percentValue" type="int" value="76" />
                     </serializedBean>
                   </action>
 
-                  <action name="PHYLIP (.phy) file association" id="10053" customizedId="PHYLIP-phy-10053-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .phy file association">
+                  <action name="PHYLIP (.phy) file association" id="10059" customizedId="PHYLIP-phy-10059-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .phy file association">
                     <serializedBean>
                       <property name="description" type="string">PHYLIP File</property>
                       <property name="extension" type="string">phy</property>
 <!-- END -->
 
 <!-- PileUp (.pileup) BEGIN -->
-                  <action name="PileUp (.pileup) message" id="10054" customizedId="PileUp-pileup-10054-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PileUp (.pileup) message" id="10060" customizedId="PileUp-pileup-10060-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">PileUp (.pileup)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="PileUp (.pileup) progress bar 78" id="10055" customizedId="PileUp-pileup-10055-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PileUp (.pileup) progress bar 80" id="10061" customizedId="PileUp-pileup-10061-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="78" />
+                      <property name="percentValue" type="int" value="80" />
                     </serializedBean>
                   </action>
 
-                  <action name="PileUp (.pileup) file association" id="10056" customizedId="PileUp-pileup-10056-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pileup file association">
+                  <action name="PileUp (.pileup) file association" id="10062" customizedId="PileUp-pileup-10062-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pileup file association">
                     <serializedBean>
                       <property name="description" type="string">PileUp File</property>
                       <property name="extension" type="string">pileup</property>
 <!-- END -->
 
 <!-- PIR (.pir) BEGIN -->
-                  <action name="PIR (.pir) message" id="10057" customizedId="PIR-pir-10057-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PIR (.pir) message" id="10063" customizedId="PIR-pir-10063-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">PIR (.pir)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="PIR (.pir) progress bar 82" id="10058" customizedId="PIR-pir-10058-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="PIR (.pir) progress bar 84" id="10064" customizedId="PIR-pir-10064-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="82" />
+                      <property name="percentValue" type="int" value="84" />
                     </serializedBean>
                   </action>
 
-                  <action name="PIR (.pir) file association" id="10059" customizedId="PIR-pir-10059-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pir file association">
+                  <action name="PIR (.pir) file association" id="10065" customizedId="PIR-pir-10065-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .pir file association">
                     <serializedBean>
                       <property name="description" type="string">PIR File</property>
                       <property name="extension" type="string">pir</property>
 <!-- END -->
 
 <!-- RNAML (.rnaml) BEGIN -->
-                  <action name="RNAML (.rnaml) message" id="10060" customizedId="RNAML-rnaml-10060-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="RNAML (.rnaml) message" id="10066" customizedId="RNAML-rnaml-10066-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">RNAML (.rnaml)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="RNAML (.rnaml) progress bar 86" id="10061" customizedId="RNAML-rnaml-10061-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="RNAML (.rnaml) progress bar 88" id="10067" customizedId="RNAML-rnaml-10067-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="86" />
+                      <property name="percentValue" type="int" value="88" />
                     </serializedBean>
                   </action>
 
-                  <action name="RNAML (.rnaml) file association" id="10062" customizedId="RNAML-rnaml-10062-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .rnaml file association">
+                  <action name="RNAML (.rnaml) file association" id="10068" customizedId="RNAML-rnaml-10068-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .rnaml file association">
                     <serializedBean>
                       <property name="description" type="string">RNAML File</property>
                       <property name="extension" type="string">rnaml</property>
 <!-- END -->
 
 <!-- Substitution Matrix (.mat) BEGIN -->
-                  <action name="Substitution Matrix (.mat) message" id="10063" customizedId="Substitution Matrix-mat-10063-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Substitution Matrix (.mat) message" id="10069" customizedId="Substitution Matrix-mat-10069-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Substitution Matrix (.mat)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Substitution Matrix (.mat) progress bar 91" id="10064" customizedId="Substitution Matrix-mat-10064-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Substitution Matrix (.mat) progress bar 92" id="10070" customizedId="Substitution Matrix-mat-10070-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="91" />
+                      <property name="percentValue" type="int" value="92" />
                     </serializedBean>
                   </action>
 
-                  <action name="Substitution Matrix (.mat) file association" id="10065" customizedId="Substitution Matrix-mat-10065-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .mat file association">
+                  <action name="Substitution Matrix (.mat) file association" id="10071" customizedId="Substitution Matrix-mat-10071-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .mat file association">
                     <serializedBean>
                       <property name="description" type="string">Substitution Matrix File</property>
                       <property name="extension" type="string">mat</property>
 <!-- END -->
 
 <!-- Stockholm (.stk, .sto) BEGIN -->
-                  <action name="Stockholm (.stk, .sto) message" id="10066" customizedId="Stockholm-stk,sto-10066-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Stockholm (.stk, .sto) message" id="10072" customizedId="Stockholm-stk,sto-10072-message" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
                       <property name="detailMessage" type="string">Stockholm (.stk, .sto)</property>
                       <property name="statusMessage" type="string">Creating file associations...</property>
                     </serializedBean>
                   </action>
 
-                  <action name="Stockholm (.stk, .sto) progress bar 95" id="10067" customizedId="Stockholm-stk,sto-10067-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
+                  <action name="Stockholm (.stk, .sto) progress bar 96" id="10073" customizedId="Stockholm-stk,sto-10073-progressbar" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="percentValue" type="int" value="95" />
+                      <property name="percentValue" type="int" value="96" />
                     </serializedBean>
                   </action>
 
-                  <action name="Stockholm (.stk, .sto) file association" id="10068" customizedId="Stockholm-stk,sto-10068-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .stk,sto file association">
+                  <action name="Stockholm (.stk, .sto) file association" id="10074" customizedId="Stockholm-stk,sto-10074-fileassociation" beanClass="com.install4j.runtime.beans.actions.desktop.CreateFileAssociationAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make .stk,sto file association">
                     <serializedBean>
                       <property name="description" type="string">Stockholm File</property>
                       <property name="extension" type="string">stk,sto</property>
index b19ddf9..a14900c 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<install4j version="8.0.5" transformSequenceNumber="8">
-  <directoryPresets config="../.." />
+<install4j version="8.0.11" transformSequenceNumber="8">
+  <directoryPresets config="bin/jalview" />
   <application name="${compiler:JALVIEW_APPLICATION_NAME}" applicationId="${compiler:WINDOWS_APPLICATION_ID}" mediaDir="${compiler:BUILD_DIR}" lzmaCompression="true" shortName="${compiler:INTERNAL_ID}" publisher="University of Dundee" publisherWeb="https://www.jalview.org/" version="${compiler:JALVIEW_VERSION}" allPathsRelative="true" macVolumeId="5aac4968c304f65" javaMinVersion="${compiler:JAVA_MIN_VERSION}" javaMaxVersion="9999999999${compiler:JAVA_MAX_VERSION}" allowBetaVM="true" jdkMode="jdk" jdkName="JDK 11.0">
     <searchSequence>
       <directory location="${compiler:JRE_DIR}" />
@@ -13,6 +13,7 @@
       <variable name="JALVIEW_DIR" value="../.." />
       <variable name="BUILD_DIR" value="${compiler:JALVIEW_DIR}/build/install4j" />
       <variable name="OSX_KEYSTORE" />
+      <variable name="OSX_APPLEID" />
       <variable name="JSIGN_SH" value="echo" />
       <variable name="JRE_DIR" value="jre" description="The folder under the app folder that the JRE will be either copied or unpacked into" />
       <variable name="INSTALLER_TEMPLATE_VERSION" value="DEVELOPMENT_default" />
       <variable name="BUNDLE_ID" value="org.jalview.jalview-desktop" />
       <variable name="INTERNAL_ID" value="Jalview" />
       <variable name="WINDOWS_APPLICATION_ID" value="6595-2347-1923-0725" />
-      <variable name="MACOS_DMG_DS_STORE" value="DS_Store" />
-      <variable name="MACOS_DMG_BG_IMAGE" />
+      <variable name="MACOS_DMG_DS_STORE" value="jalview_dmg_DS_Store" />
+      <variable name="MACOS_DMG_BG_IMAGE" value="jalview_dmg_background-72dpi.png" />
+      <variable name="WRAPPER_LINK" value="jalview" />
+      <variable name="BASH_WRAPPER_SCRIPT" value="jalview.sh" />
+      <variable name="WRAPPER_SCRIPT_BIN_DIR" value="bin" />
       <variable name="INSTALLER_NAME" value="Jalview Installer" />
       <variable name="INSTALL4J_UTILS_DIR" value="utils/install4j" />
       <variable name="GETDOWN_WEBSITE_DIR" value="getdown/website" />
       <variable name="PNG_ICON_FILE" value="utils/channels/release/images/jalview_logo.png" />
       <variable name="BACKGROUND" value="utils/channels/release/images/jalview_logo_background_fade-640x480.png" />
     </variables>
-    <codeSigning macEnabled="true" macPkcs12File="${compiler:OSX_KEYSTORE}" />
+    <codeSigning macEnabled="true" macPkcs12File="${compiler:OSX_KEYSTORE}" macNotarize="true" appleId="${compiler:OSX_APPLEID}">
+      <macAdditionalBinaries>
+        <entry>*.dylib</entry>
+        <entry>*.so</entry>
+        <entry>*.jnilib</entry>
+        <entry>unpack200</entry>
+        <entry>tnameserv</entry>
+        <entry>servertool</entry>
+        <entry>rmiregistry</entry>
+        <entry>rmid</entry>
+        <entry>policytool</entry>
+        <entry>pack200</entry>
+        <entry>orbd</entry>
+        <entry>keytool</entry>
+        <entry>jjs</entry>
+        <entry>java</entry>
+        <entry>jspawnhelper</entry>
+        <entry>libfreetype.dylib.6</entry>
+        <entry>applet</entry>
+        <entry>jaotc</entry>
+        <entry>jfr</entry>
+        <entry>jrunscript</entry>
+        <entry>libjli.dylib</entry>
+      </macAdditionalBinaries>
+    </codeSigning>
   </application>
   <files defaultUninstallMode="2">
     <filesets>
       <fileset name="Mac OS X JRE" id="880" />
       <fileset name="Windows JRE" id="882" />
       <fileset name="Jalview application" id="1873" />
-      <fileset name="MacOS Old Jalview Uninstaller" id="2105" />
     </filesets>
     <roots>
       <root id="735" fileset="734" />
       <root id="881" fileset="880" />
       <root id="883" fileset="882" />
       <root id="1874" fileset="1873" />
-      <root id="2106" fileset="2105" />
     </roots>
     <mountPoints>
       <mountPoint id="454" />
       <mountPoint id="884" root="881" />
       <mountPoint id="885" root="883" />
       <mountPoint id="1875" root="1874" />
-      <mountPoint id="2107" root="2106" />
     </mountPoints>
     <entries>
       <dirEntry mountPoint="454" file="${compiler:JALVIEW_DIR}/${compiler:GETDOWN_FILES_DIR}/${compiler:JAVA_VERSION}" uninstallMode="2" overrideOverwriteMode="true" overrideUninstallMode="true" subDirectory="files" />
-      <dirEntry mountPoint="736" file="${compiler:JALVIEW_DIR}/${compiler:GETDOWN_WEBSITE_DIR}/${compiler:JAVA_VERSION}" uninstallMode="2" overrideOverwriteMode="true" overrideUninstallMode="true" subDirectory="files" />
+      <dirEntry mountPoint="736" file="${compiler:JALVIEW_DIR}/${compiler:GETDOWN_WEBSITE_DIR}/${compiler:JAVA_VERSION}" uninstallMode="2" overrideOverwriteMode="true" overrideUninstallMode="true" subDirectory="files">
+        <exclude>
+          <entry location="${compiler:WRAPPER_SCRIPT_BIN_DIR}" />
+        </exclude>
+      </dirEntry>
       <dirEntry mountPoint="736" file="${compiler:JALVIEW_DIR}/examples" overwriteMode="1" uninstallMode="2" overrideFileMode="true" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="subdir" subDirectory="examples" />
+      <dirEntry mountPoint="736" file="${compiler:JALVIEW_DIR}/${compiler:GETDOWN_WEBSITE_DIR}/${compiler:JAVA_VERSION}/${compiler:WRAPPER_SCRIPT_BIN_DIR}" fileMode="755" overrideFileMode="true" overrideUninstallMode="true" entryMode="subdir" subDirectory="${compiler:WRAPPER_SCRIPT_BIN_DIR}" overrideDirMode="true" />
       <dirEntry mountPoint="884" file="${compiler:MACOS_JAVA_VM_DIR}" fileMode="755" overrideFileMode="true" overrideUninstallMode="true" entryMode="subdir" subDirectory="${compiler:JRE_DIR}" />
       <dirEntry mountPoint="885" file="${compiler:WINDOWS_JAVA_VM_DIR}" fileMode="755" overrideFileMode="true" overrideUninstallMode="true" entryMode="subdir" subDirectory="${compiler:JRE_DIR}" />
       <dirEntry mountPoint="1875" file="${compiler:JALVIEW_DIR}/${compiler:GETDOWN_WEBSITE_DIR}/${compiler:JAVA_VERSION}/${compiler:GETDOWN_DIST_DIR}" overwriteMode="1" uninstallMode="2" overrideFileMode="true" overrideOverwriteMode="true" overrideUninstallMode="true" entryMode="subdir" subDirectory="${compiler:GETDOWN_DIST_DIR}" overrideDirMode="true" />
-      <dirEntry mountPoint="2107" file="Uninstall Old Jalview.app" fileMode="755" overrideFileMode="true" entryMode="subdir" subDirectory="Uninstall Old Jalview.app" overrideDirMode="true" />
     </entries>
     <components>
       <component name="jalview_getdown" id="1031">
           <entry filesetId="1873" />
         </include>
       </component>
-      <component name="macos_old_jalview_uninstaller" id="2110">
-        <include>
-          <entry filesetId="2105" />
-        </include>
-      </component>
     </components>
   </files>
   <launchers>
                   <property name="obtainIfAdminWin" type="boolean" value="false" />
                 </serializedBean>
               </action>
+              <action name="Set unixUserBinDir (Linux or Unix)" id="2738" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" rollbackBarrierExitCode="0">
+                <serializedBean>
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">String userHome = (String)context.getVariable("sys.userHome");
+
+String[] tryPaths = new String[] {
+    "bin",
+    ".local" + File.separator + "bin",
+    "local" + File.separator + "bin",
+    "opt" + File.separator + "bin"
+};
+
+for (int i = 0; i &lt; tryPaths.length; i++) {
+    String tryPath = tryPaths[i];
+    File unixUserBinDir = new File(userHome + File.separator + tryPath);
+    if (unixUserBinDir.exists()) {
+        return tryPath;
+    }
+}
+
+return null;
+</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">unixUserBinDir</property>
+                </serializedBean>
+                <condition>Util.isLinux() || Util.isUnixInstaller()</condition>
+              </action>
             </actions>
           </screen>
         </startup>
@@ -466,7 +519,7 @@ return console.askOkCancel(message, true);
                   </action>
                   <action id="2542" beanClass="com.install4j.runtime.beans.actions.control.SetMessageAction" actionElevationType="none" rollbackBarrierExitCode="0">
                     <serializedBean>
-                      <property name="statusMessage" type="string">Creating file associations</property>
+                      <property name="statusMessage" type="string">Finished creating file associations</property>
                       <property name="useDetail" type="boolean" value="true" />
                       <property name="useStatus" type="boolean" value="true" />
                     </serializedBean>
@@ -564,6 +617,44 @@ return console.askOkCancel(message, true);
                 </serializedBean>
                 <condition>context.getBooleanVariable("addToDockAction")</condition>
               </action>
+              <action name="Linux/Unix symlink" id="2733" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make symlink to wrapper script">
+                <serializedBean>
+                  <property name="file">
+                    <object class="java.io.File">
+                      <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_WRAPPER_SCRIPT}</string>
+                    </object>
+                  </property>
+                  <property name="linkFile">
+                    <object class="java.io.File">
+                      <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:WRAPPER_LINK}</string>
+                    </object>
+                  </property>
+                </serializedBean>
+                <condition>!Util.isWindows()</condition>
+              </action>
+              <action name="Add Jalview bin to the user's path (Windows)" id="2740" beanClass="com.install4j.runtime.beans.actions.misc.ModifyEnvironmentVariableAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not add &quot;${installer:sys.contentDir}\${compiler:WRAPPER_SCRIPT_BIN_DIR}&quot; to the Path environment variable">
+                <serializedBean>
+                  <property name="type" type="enum" class="com.install4j.runtime.beans.actions.misc.ModifyStringType" value="APPEND" />
+                  <property name="value" type="string">${installer:sys.contentDir}\${compiler:WRAPPER_SCRIPT_BIN_DIR}</property>
+                  <property name="variableName" type="string">Path</property>
+                </serializedBean>
+                <condition>context.getBooleanVariable("appendToPathAction")</condition>
+              </action>
+              <action name="Create symbolic link to jalview.sh in user's local bin" id="2739" beanClass="com.install4j.runtime.beans.actions.files.CreateSymlinkAction" actionElevationType="elevated" rollbackBarrierExitCode="0" errorMessage="Could not make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:unixUserBinDir}">
+                <serializedBean>
+                  <property name="file">
+                    <object class="java.io.File">
+                      <string>${installer:sys.contentDir}/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_WRAPPER_SCRIPT}</string>
+                    </object>
+                  </property>
+                  <property name="linkFile">
+                    <object class="java.io.File">
+                      <string>${installer:sys.userHome}/${installer:unixUserBinDir}/${compiler:WRAPPER_LINK}</string>
+                    </object>
+                  </property>
+                </serializedBean>
+                <condition>context.getBooleanVariable("makeSymbolicLink") &amp;&amp; ( Util.isLinux() || Util.isUnixInstaller() ) &amp;&amp; ( context.getVariable("unixUserBinDir") != null )</condition>
+              </action>
             </actions>
             <formComponents>
               <formComponent id="21" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" insetBottom="10">
@@ -586,6 +677,22 @@ return console.askOkCancel(message, true);
                 </serializedBean>
                 <visibilityScript>Util.isMacOS()</visibilityScript>
               </formComponent>
+              <formComponent name="Add jalview bin to the user's Path environment variable (Windows)" id="2734" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                <serializedBean>
+                  <property name="checkboxText" type="string">Add ${compiler:JALVIEW_APPLICATION_NAME}'s bin folder to the Path environment variable</property>
+                  <property name="initiallySelected" type="boolean" value="true" />
+                  <property name="variableName" type="string">appendToPathAction</property>
+                </serializedBean>
+                <visibilityScript>Util.isWindows()</visibilityScript>
+              </formComponent>
+              <formComponent name="Make a symbolic link to jalview.sh bash script in the user's local bin (Linux or Unix)" id="2736" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                <serializedBean>
+                  <property name="checkboxText" type="string">Make a ${compiler:WRAPPER_LINK} symbolic link in ~/${installer:unixUserBinDir}</property>
+                  <property name="initiallySelected" type="boolean" value="true" />
+                  <property name="variableName" type="string">makeSymbolicLink</property>
+                </serializedBean>
+                <visibilityScript>( Util.isLinux() || Util.isUnixInstaller() ) &amp;&amp; ( context.getVariable("unixUserBinDir") != null )</visibilityScript>
+              </formComponent>
             </formComponents>
           </screen>
         </screens>
@@ -688,7 +795,7 @@ return console.askYesNo(message, true);
               </action>
               <action id="1525" beanClass="com.install4j.runtime.beans.actions.files.DeleteFileAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
                 <serializedBean>
-                  <property name="files" type="array" class="java.io.File" length="36">
+                  <property name="files" type="array" class="java.io.File" length="40">
                     <element index="0">
                       <object class="java.io.File">
                         <string>jre</string>
@@ -869,6 +976,26 @@ return console.askYesNo(message, true);
                         <string>build_*</string>
                       </object>
                     </element>
+                    <element index="36">
+                      <object class="java.io.File">
+                        <string>${compiler:WRAPPER_SCRIPT_BIN_DIR}</string>
+                      </object>
+                    </element>
+                    <element index="37">
+                      <object class="java.io.File">
+                        <string>bin</string>
+                      </object>
+                    </element>
+                    <element index="38">
+                      <object class="java.io.File">
+                        <string>channel.props</string>
+                      </object>
+                    </element>
+                    <element index="39">
+                      <object class="java.io.File">
+                        <string>channel.propsv</string>
+                      </object>
+                    </element>
                   </property>
                   <property name="recursive" type="boolean" value="true" />
                 </serializedBean>
@@ -1134,7 +1261,6 @@ return console.askYesNo(message, true);
         <component id="1156" />
         <component id="1276" />
         <component id="1881" />
-        <component id="2110" />
       </excludedComponents>
       <exclude>
         <entry defaultFileset="true" />
@@ -1162,7 +1288,7 @@ return console.askYesNo(message, true);
         <file name=".DS_Store" file="${compiler:JALVIEW_DIR}/${compiler:MACOS_DMG_DS_STORE}" />
         <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/Jalview-File.icns" file="${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/Jalview-File.icns" />
         <file name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/Jalview-Launch.icns" file="${compiler:JALVIEW_DIR}/${compiler:INSTALL4J_UTILS_DIR}/Jalview-Launch.icns" />
-        <symlink name="Uninstall Old Jalview (optional).app" target="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/Resources/app/Uninstall Old Jalview.app" />
+        <symlink name="${compiler:JALVIEW_APPLICATION_NAME}.app/Contents/MacOS/${compiler:WRAPPER_LINK}" target="../Resources/app/${compiler:WRAPPER_SCRIPT_BIN_DIR}/${compiler:BASH_WRAPPER_SCRIPT}" />
       </topLevelFiles>
     </macosArchive>
     <unixInstaller name="Linux x64 Shell Installer" id="1595" mediaFileName="${compiler:UNIX_APPLICATION_FOLDER}-${compiler:JALVIEW_VERSION}-linux_x64-java_${compiler:JAVA_INTEGER_VERSION}" installDir="${compiler:UNIX_APPLICATION_FOLDER}" customInstallBaseDir="~/opt/">
@@ -1171,7 +1297,6 @@ return console.askYesNo(message, true);
         <component id="1156" />
         <component id="1276" />
         <component id="1881" />
-        <component id="2110" />
       </excludedComponents>
       <exclude>
         <entry defaultFileset="true" />
@@ -1188,7 +1313,6 @@ return console.askYesNo(message, true);
         <component id="1156" />
         <component id="1276" />
         <component id="1881" />
-        <component id="2110" />
       </excludedComponents>
       <exclude>
         <entry defaultFileset="true" />
@@ -1205,7 +1329,6 @@ return console.askYesNo(message, true);
         <component id="1156" />
         <component id="1276" />
         <component id="1881" />
-        <component id="2110" />
       </excludedComponents>
       <exclude>
         <entry defaultFileset="true" />
index ae01fe1..fd38213 100644 (file)
-
-com/stevesoft/pat/Any.js
-com/stevesoft/pat/Bracket.js
-com/stevesoft/pat/Branch.js
-com/stevesoft/pat/CaseMgr.js
-com/stevesoft/pat/DotMulti.js
-com/stevesoft/pat/End.js
-com/stevesoft/pat/FastBracket.js
-com/stevesoft/pat/FastChar.js
-com/stevesoft/pat/FastMulti.js
-com/stevesoft/pat/NUnicodeAlpha.js
-com/stevesoft/pat/NUnicodeCurrency.js
-com/stevesoft/pat/NUnicodeDigit.js
-com/stevesoft/pat/NUnicodeMath.js
-com/stevesoft/pat/NUnicodePunct.js
-com/stevesoft/pat/NUnicodeW.js
-com/stevesoft/pat/NUnicodeWhite.js
-com/stevesoft/pat/NoPattern.js
-com/stevesoft/pat/NullPattern.js
-com/stevesoft/pat/Or.js
-com/stevesoft/pat/OrMark.js
-com/stevesoft/pat/Pattern.js
-com/stevesoft/pat/PatternSub.js
-com/stevesoft/pat/Pthings.js
-com/stevesoft/pat/Range.js
-com/stevesoft/pat/RegHolder.js
-com/stevesoft/pat/RegOpt.js
-com/stevesoft/pat/RegRes.js
-com/stevesoft/pat/Regex.js
-com/stevesoft/pat/ReplaceRule.js
-com/stevesoft/pat/Replacer.js
-com/stevesoft/pat/Rthings.js
-com/stevesoft/pat/Skip.js
-com/stevesoft/pat/StrPos.js
-com/stevesoft/pat/StringLike.js
-com/stevesoft/pat/StringRule.js
-com/stevesoft/pat/SubMark.js
-com/stevesoft/pat/UniValidator.js
-com/stevesoft/pat/UnicodeAlpha.js
-com/stevesoft/pat/UnicodeCurrency.js
-com/stevesoft/pat/UnicodeDigit.js
-com/stevesoft/pat/UnicodeLower.js
-com/stevesoft/pat/UnicodeMath.js
-com/stevesoft/pat/UnicodePunct.js
-com/stevesoft/pat/UnicodeUpper.js
-com/stevesoft/pat/UnicodeW.js
-com/stevesoft/pat/UnicodeWhite.js
-com/stevesoft/pat/Validator.js
-com/stevesoft/pat/oneChar.js
-com/stevesoft/pat/parsePerl.js
-com/stevesoft/pat/patInf.js
-com/stevesoft/pat/patInt.js
-com/stevesoft/pat/wrap/StringWrap.js
+com/stevesoft/pat/Boundary.js
+com/stevesoft/pat/Start.js
 intervalstore/api/IntervalI.js
 intervalstore/api/IntervalStoreI.js
 intervalstore/impl/BinarySearcher.js
 intervalstore/impl/IntervalStore.js
 intervalstore/impl/NCList.js
 intervalstore/impl/NCNode.js
-jalview/analysis/AAFrequency.js
-jalview/analysis/AlignSeq.js
-jalview/analysis/AlignmentAnnotationUtils.js
-jalview/analysis/AlignmentUtils.js
-jalview/analysis/AnnotationSorter.js
-jalview/analysis/AverageDistanceTree.js
-jalview/analysis/Conservation.js
-jalview/analysis/CrossRef.js
-jalview/analysis/GeneticCodeI.js
-jalview/analysis/GeneticCodes.js
-jalview/analysis/Grouping.js
-jalview/analysis/NJTree.js
-jalview/analysis/SeqsetUtils.js
-jalview/analysis/SequenceIdMatcher.js
-jalview/analysis/TreeBuilder.js
-jalview/analysis/TreeModel.js
-jalview/analysis/scoremodels/DistanceScoreModel.js
-jalview/analysis/scoremodels/FeatureDistanceModel.js
-jalview/analysis/scoremodels/PIDModel.js
-jalview/analysis/scoremodels/ScoreMatrix.js
-jalview/analysis/scoremodels/ScoreModels.js
-jalview/analysis/scoremodels/SimilarityParams.js
-jalview/analysis/scoremodels/SimilarityScoreModel.js
-jalview/api/AlignCalcManagerI.js
-jalview/api/AlignCalcWorkerI.js
-jalview/api/AlignViewControllerGuiI.js
-jalview/api/AlignViewControllerI.js
-jalview/api/AlignViewportI.js
-jalview/api/AlignmentColsCollectionI.js
-jalview/api/AlignmentRowsCollectionI.js
-jalview/api/AlignmentViewPanel.js
-jalview/api/BuildDetailsI.js
-jalview/api/DBRefEntryI.js
-jalview/api/FeatureColourI.js
-jalview/api/FeatureRenderer.js
-jalview/api/FeatureSettingsControllerI.js
-jalview/api/FeatureSettingsModelI.js
-jalview/api/FeaturesDisplayedI.js
-jalview/api/OOMHandlerI.js
-jalview/api/RendererListenerI.js
-jalview/api/SequenceRenderer.js
-jalview/api/SequenceStructureBinding.js
-jalview/api/StructureSelectionManagerProvider.js
-jalview/api/ViewStyleI.js
-jalview/api/analysis/PairwiseScoreModelI.js
-jalview/api/analysis/ScoreModelI.js
-jalview/api/analysis/SimilarityParamsI.js
-jalview/api/structures/JalviewStructureDisplayI.js
-jalview/bin/ArgsParser.js
-jalview/bin/BuildDetails.js
-jalview/bin/Cache.js
-jalview/bin/Jalview.js
-jalview/controller/AlignViewController.js
-jalview/datamodel/ASequence.js
-jalview/datamodel/ASequenceI.js
-jalview/datamodel/Alignment.js
-jalview/datamodel/AlignmentAnnotation.js
-jalview/datamodel/AlignmentI.js
-jalview/datamodel/AlignmentView.js
-jalview/datamodel/AnnotatedCollectionI.js
-jalview/datamodel/Annotation.js
-jalview/datamodel/BinaryNode.js
-jalview/datamodel/CigarArray.js
-jalview/datamodel/CigarBase.js
-jalview/datamodel/CigarSimple.js
-jalview/datamodel/ColumnSelection.js
-jalview/datamodel/ContiguousI.js
-jalview/datamodel/DBRefEntry.js
-jalview/datamodel/DBRefSource.js
-jalview/datamodel/GraphLine.js
-jalview/datamodel/HiddenColumns.js
-jalview/datamodel/HiddenColumnsCursor.js
-jalview/datamodel/HiddenCursorPosition.js
-jalview/datamodel/HiddenSequences.js
-jalview/datamodel/Mapping.js
-jalview/datamodel/PDBEntry.js
-jalview/datamodel/Profile.js
-jalview/datamodel/ProfileI.js
-jalview/datamodel/Profiles.js
-jalview/datamodel/ProfilesI.js
-jalview/datamodel/Range.js
-jalview/datamodel/RangeElementsIterator.js
-jalview/datamodel/RangeIterator.js
-jalview/datamodel/ResidueCount.js
-jalview/datamodel/SearchResultMatchI.js
-jalview/datamodel/SearchResults.js
-jalview/datamodel/SearchResultsI.js
-jalview/datamodel/SeqCigar.js
-jalview/datamodel/Sequence.js
-jalview/datamodel/SequenceCollectionI.js
-jalview/datamodel/SequenceCursor.js
-jalview/datamodel/SequenceFeature.js
-jalview/datamodel/SequenceGroup.js
-jalview/datamodel/SequenceI.js
-jalview/datamodel/SequenceNode.js
-jalview/datamodel/StartRegionIterator.js
-jalview/datamodel/StructureViewerModel.js
-jalview/datamodel/VisibleColsCollection.js
-jalview/datamodel/VisibleContigsIterator.js
-jalview/datamodel/VisibleRowsCollection.js
-jalview/datamodel/VisibleRowsIterator.js
-jalview/datamodel/features/FeatureAttributes.js
-jalview/datamodel/features/FeatureLocationI.js
-jalview/datamodel/features/FeatureMatcherSet.js
-jalview/datamodel/features/FeatureMatcherSetI.js
-jalview/datamodel/features/FeatureSources.js
-jalview/datamodel/features/FeatureStore.js
-jalview/datamodel/features/SequenceFeatures.js
-jalview/datamodel/features/SequenceFeaturesI.js
-jalview/ext/ensembl/EnsemblData.js
-jalview/ext/ensembl/EnsemblGene.js
-jalview/ext/ensembl/EnsemblGenomes.js
-jalview/ext/ensembl/EnsemblRestClient.js
-jalview/ext/ensembl/EnsemblSeqProxy.js
-jalview/ext/ensembl/EnsemblSequenceFetcher.js
-jalview/ext/jmol/JalviewJmolBinding.js
-jalview/ext/jmol/JmolCommands.js
-jalview/ext/jmol/JmolParser.js
-jalview/fts/api/FTSData.js
-jalview/fts/api/FTSDataColumnI.js
-jalview/fts/api/FTSRestClientI.js
-jalview/fts/api/GFTSPanelI.js
-jalview/fts/core/DecimalFormatTableCellRenderer.js
-jalview/fts/core/FTSDataColumnPreferences.js
-jalview/fts/core/FTSRestClient.js
-jalview/fts/core/FTSRestRequest.js
-jalview/fts/core/FTSRestResponse.js
-jalview/fts/core/GFTSPanel.js
-jalview/fts/service/pdb/PDBFTSPanel.js
-jalview/fts/service/pdb/PDBFTSRestClient.js
-jalview/fts/service/uniprot/UniProtFTSRestClient.js
-jalview/fts/service/uniprot/UniprotFTSPanel.js
-jalview/gui/AlignFrame.js
-jalview/gui/AlignViewport.js
-jalview/gui/AlignmentPanel.js
-jalview/gui/AnnotationColumnChooser.js
-jalview/gui/AnnotationLabels.js
-jalview/gui/AnnotationPanel.js
-jalview/gui/AnnotationRowFilter.js
-jalview/gui/AppJmol.js
-jalview/gui/AppJmolBinding.js
-jalview/gui/CalculationChooser.js
-jalview/gui/ColourMenuHelper.js
-jalview/gui/ComboBoxTooltipRenderer.js
-jalview/gui/Desktop.js
-jalview/gui/FeatureRenderer.js
-jalview/gui/FeatureSettings.js
-jalview/gui/FeatureTypeSettings.js
-jalview/gui/FontChooser.js
-jalview/gui/IProgressIndicator.js
-jalview/gui/IdCanvas.js
-jalview/gui/IdPanel.js
-jalview/gui/IdwidthAdjuster.js
-jalview/gui/JalviewBooleanRadioButtons.js
-jalview/gui/JalviewChangeSupport.js
-jalview/gui/JalviewColourChooser.js
-jalview/gui/JalviewDialog.js
-jalview/gui/JvSwingUtils.js
-jalview/gui/OverviewCanvas.js
-jalview/gui/OverviewPanel.js
-jalview/gui/PaintRefresher.js
-jalview/gui/PopupMenu.js
-jalview/gui/Preferences.js
-jalview/gui/ProgressBar.js
-jalview/gui/ProgressPanel.js
-jalview/gui/ScalePanel.js
-jalview/gui/SeqCanvas.js
-jalview/gui/SeqPanel.js
-jalview/gui/SequenceFetcher.js
-jalview/gui/SequenceRenderer.js
-jalview/gui/SliderPanel.js
-jalview/gui/StructureChooser.js
-jalview/gui/StructureViewer.js
-jalview/gui/StructureViewerBase.js
-jalview/gui/TreeCanvas.js
-jalview/gui/TreePanel.js
-jalview/gui/ViewSelectionMenu.js
-jalview/io/AlignFile.js
-jalview/io/AlignmentFileReaderI.js
-jalview/io/AlignmentFileWriterI.js
-jalview/io/AnnotationFile.js
-jalview/io/AppletFormatAdapter.js
-jalview/io/DataSourceType.js
-jalview/io/FastaFile.js
-jalview/io/FileFormat.js
-jalview/io/FileFormatI.js
-jalview/io/FileFormats.js
-jalview/io/FileLoader.js
-jalview/io/FileParse.js
-jalview/io/FormatAdapter.js
-jalview/io/IdentifyFile.js
-jalview/io/JPredFile.js
-jalview/io/JalviewFileChooser.js
-jalview/io/JalviewFileFilter.js
-jalview/io/JalviewFileView.js
-jalview/io/JnetAnnotationMaker.js
-jalview/io/NewickFile.js
-jalview/io/PDBFeatureSettings.js
-jalview/io/PIRFile.js
-jalview/io/ScoreMatrixFile.js
-jalview/io/SequenceAnnotationReport.js
-jalview/io/StructureFile.js
-jalview/io/TCoffeeScoreFile.js
-jalview/io/cache/JvCacheableInputBox.js
-jalview/javascript/json/JSON.js
-jalview/javascript/log4j/ConsoleAppender.js
-jalview/javascript/log4j/Layout.js
-jalview/javascript/log4j/Level.js
-jalview/javascript/log4j/Logger.js
-jalview/javascript/log4j/Priority.js
-jalview/javascript/log4j/SimpleLayout.js
-jalview/javascript/log4j/spi/OptionHandler.js
-jalview/javascript/web/Client.js
-jalview/javascript/web/ClientResponse.js
-jalview/javascript/web/WebResource.js
-jalview/jbgui/BackupFilesPresetEntry.js
-jalview/jbgui/GAlignFrame.js
-jalview/jbgui/GAlignmentPanel.js
-jalview/jbgui/GDesktop.js
-jalview/jbgui/GFontChooser.js
-jalview/jbgui/GPreferences.js
-jalview/jbgui/GSliderPanel.js
-jalview/jbgui/GStructureChooser.js
-jalview/jbgui/GStructureViewer.js
-jalview/jbgui/GTreePanel.js
-jalview/jbgui/IntKeyStringValueEntry.js
-jalview/math/Matrix.js
-jalview/math/MatrixI.js
-jalview/project/Jalview2XML.js
-jalview/renderer/AnnotationRenderer.js
-jalview/renderer/AwtRenderPanelI.js
-jalview/renderer/OverviewRenderer.js
-jalview/renderer/OverviewResColourFinder.js
-jalview/renderer/ResidueColourFinder.js
-jalview/renderer/ResidueShader.js
-jalview/renderer/ResidueShaderI.js
-jalview/renderer/ScaleRenderer.js
-jalview/renderer/seqfeatures/FeatureColourFinder.js
-jalview/renderer/seqfeatures/FeatureRenderer.js
-jalview/schemes/Blosum62ColourScheme.js
-jalview/schemes/BuriedColourScheme.js
-jalview/schemes/ClustalxColourScheme.js
-jalview/schemes/ColourSchemeI.js
-jalview/schemes/ColourSchemeProperty.js
-jalview/schemes/ColourSchemes.js
-jalview/schemes/Consensus.js
-jalview/schemes/FeatureColour.js
-jalview/schemes/FeatureSettingsAdapter.js
-jalview/schemes/HelixColourScheme.js
-jalview/schemes/HydrophobicColourScheme.js
-jalview/schemes/IdColourScheme.js
-jalview/schemes/JalviewColourScheme.js
-jalview/schemes/NucleotideColourScheme.js
-jalview/schemes/PIDColourScheme.js
-jalview/schemes/PurinePyrimidineColourScheme.js
-jalview/schemes/RNAHelicesColour.js
-jalview/schemes/ResidueColourScheme.js
-jalview/schemes/ResidueProperties.js
-jalview/schemes/ScoreColourScheme.js
-jalview/schemes/StrandColourScheme.js
-jalview/schemes/TCoffeeColourScheme.js
-jalview/schemes/TaylorColourScheme.js
-jalview/schemes/TurnColourScheme.js
-jalview/schemes/ZappoColourScheme.js
-jalview/structure/AtomSpec.js
-jalview/structure/CommandListener.js
-jalview/structure/SelectionListener.js
-jalview/structure/SelectionSource.js
-jalview/structure/SequenceListener.js
-jalview/structure/StructureImportSettings.js
-jalview/structure/StructureListener.js
-jalview/structure/StructureMapping.js
-jalview/structure/StructureMappingcommandSet.js
-jalview/structure/StructureSelectionManager.js
-jalview/structure/VamsasSource.js
-jalview/structures/models/AAStructureBindingModel.js
-jalview/structures/models/SequenceStructureBindingModel.js
-jalview/urls/CustomUrlProvider.js
-jalview/urls/IdOrgSettings.js
-jalview/urls/IdentifiersUrlProvider.js
-jalview/urls/UrlLinkDisplay.js
-jalview/urls/UrlLinkTableModel.js
-jalview/urls/UrlProvider.js
-jalview/urls/UrlProviderImpl.js
-jalview/urls/api/UrlProviderFactoryI.js
-jalview/urls/api/UrlProviderI.js
-jalview/urls/desktop/DesktopUrlProviderFactory.js
-jalview/util/CaseInsensitiveString.js
-jalview/util/ColorUtils.js
-jalview/util/Comparison.js
-jalview/util/DBRefUtils.js
-jalview/util/Format.js
-jalview/util/LinkedIdentityHashSet.js
-jalview/util/MapList.js
-jalview/util/MessageManager.js
-jalview/util/Platform.js
-jalview/util/QuickSort.js
-jalview/util/SetUtils.js
-jalview/util/StringUtils.js
-jalview/util/UrlLink.js
-jalview/util/dialogrunner/DialogRunnerI.js
-jalview/util/jarInputStreamProvider.js
-jalview/viewmodel/AlignmentViewport.js
-jalview/viewmodel/OverviewDimensions.js
-jalview/viewmodel/OverviewDimensionsHideHidden.js
-jalview/viewmodel/ViewportListenerI.js
-jalview/viewmodel/ViewportProperties.js
-jalview/viewmodel/ViewportRanges.js
-jalview/viewmodel/annotationfilter/AnnotationFilterParameter.js
-jalview/viewmodel/seqfeatures/FeatureRendererModel.js
-jalview/viewmodel/seqfeatures/FeatureRendererSettings.js
-jalview/viewmodel/seqfeatures/FeaturesDisplayed.js
-jalview/viewmodel/styles/ViewStyle.js
-jalview/workers/AlignCalcManager.js
-jalview/workers/AlignCalcWorker.js
-jalview/workers/ComplementConsensusThread.js
-jalview/workers/ConsensusThread.js
-jalview/workers/ConservationThread.js
-jalview/workers/StrucConsensusThread.js
-jalview/ws/SequenceFetcher.js
-jalview/ws/dbsources/EbiFileRetrievedProxy.js
-jalview/ws/dbsources/EmblCdsSource.js
-jalview/ws/dbsources/EmblSource.js
-jalview/ws/dbsources/EmblXmlSource.js
-jalview/ws/dbsources/Pdb.js
-jalview/ws/dbsources/Pfam.js
-jalview/ws/dbsources/PfamFull.js
-jalview/ws/dbsources/PfamSeed.js
-jalview/ws/dbsources/Rfam.js
-jalview/ws/dbsources/RfamSeed.js
-jalview/ws/dbsources/Uniprot.js
-jalview/ws/dbsources/Xfam.js
-jalview/ws/ebi/EBIFetchClient.js
-jalview/ws/seqfetcher/ASequenceFetcher.js
-jalview/ws/seqfetcher/DbSourceProxy.js
-jalview/ws/seqfetcher/DbSourceProxyImpl.js
-jalview/ws/sifts/SiftsSettings.js
-jalview/xml/binding/jalview/Annotation.js
-jalview/xml/binding/jalview/AnnotationElement.js
-jalview/xml/binding/jalview/Feature.js
-jalview/xml/binding/jalview/JalviewModel.js
-jalview/xml/binding/jalview/ObjectFactory.js
-jalview/xml/binding/jalview/Pdbentry.js
-jalview/xml/binding/jalview/Sequence.js
-jalview/xml/binding/jalview/SequenceSet.js
-jalview/xml/binding/jalview/SequenceType.js
-jalview/xml/binding/jalview/VAMSAS.js
-jalview/xml/binding/jalview/WebServiceParameterSet.js
-jalview/xml/binding/uniprot/CitationType.js
-jalview/xml/binding/uniprot/CommentType.js
-jalview/xml/binding/uniprot/ConsortiumType.js
-jalview/xml/binding/uniprot/DbReferenceType.js
-jalview/xml/binding/uniprot/Entry.js
-jalview/xml/binding/uniprot/EventType.js
-jalview/xml/binding/uniprot/EvidenceType.js
-jalview/xml/binding/uniprot/EvidencedStringType.js
-jalview/xml/binding/uniprot/FeatureType.js
-jalview/xml/binding/uniprot/GeneNameType.js
-jalview/xml/binding/uniprot/GeneType.js
-jalview/xml/binding/uniprot/IsoformType.js
-jalview/xml/binding/uniprot/KeywordType.js
-jalview/xml/binding/uniprot/LocationType.js
-jalview/xml/binding/uniprot/NameListType.js
-jalview/xml/binding/uniprot/OrganismNameType.js
-jalview/xml/binding/uniprot/OrganismType.js
-jalview/xml/binding/uniprot/PersonType.js
-jalview/xml/binding/uniprot/PositionType.js
-jalview/xml/binding/uniprot/PropertyType.js
-jalview/xml/binding/uniprot/ProteinExistenceType.js
-jalview/xml/binding/uniprot/ProteinType.js
-jalview/xml/binding/uniprot/ReferenceType.js
-jalview/xml/binding/uniprot/SequenceType.js
-jalview/xml/binding/uniprot/SourceDataType.js
-jalview/xml/binding/uniprot/SourceType.js
-jalview/xml/binding/uniprot/SubcellularLocationType.js
-jalview/xml/binding/uniprot/Uniprot.js
+jalview/bin/JalviewTaskbar.js
+jalview/datamodel/AlignedCodonFrame.js
+jalview/ext/ensembl/EnsemblFeatures.js
+jalview/ext/ensembl/EnsemblLookup.js
+jalview/ext/ensembl/EnsemblSymbol.js
+jalview/ext/ensembl/EnsemblXref.js
+jalview/ext/ensembl/Species.js
+jalview/fts/api/StructureFTSRestClientI.js
+jalview/fts/service/threedbeacons/TDBeaconsFTSRestClient.js
+jalview/gui/APQHandlers.js
+jalview/gui/CrossRefAction.js
+jalview/gui/CutAndPasteTransfer.js
+jalview/gui/JvOptionPane.js
+jalview/gui/SplashScreen.js
+jalview/gui/structurechooser/PDBStructureChooserQuerySource.js
+jalview/gui/structurechooser/StructureChooserQuerySource.js
+jalview/gui/structurechooser/TDBResultAnalyser.js
+jalview/gui/structurechooser/ThreeDBStructureChooserQuerySource.js
+jalview/io/gff/SequenceOntologyFactory.js
+jalview/io/gff/SequenceOntologyI.js
+jalview/io/gff/SequenceOntologyLite.js
+jalview/jbgui/FilterOption.js
+jalview/jbgui/GCutAndPasteTransfer.js
+jalview/structure/AtomSpecModel.js
+jalview/structure/StructureCommand.js
+jalview/structure/StructureCommandI.js
+jalview/structure/StructureCommandsBase.js
+jalview/structure/StructureCommandsI.js
+jalview/util/ChannelProperties.js
+jalview/util/HttpUtils.js
+jalview/util/IntRangeComparator.js
+jalview/util/JSONUtils.js
+jalview/util/ShortcutKeyMaskExWrapper.js
+jalview/util/ShortcutKeyMaskExWrapper8.js
+jalview/util/ShortcutKeyMaskExWrapperI.js
+jalview/ws/SequenceFetcherFactory.js
+jalview/ws/dbsources/EBIAlfaFold.js
+jalview/ws/dbsources/EmblFlatfileSource.js
+jalview/ws/utils/UrlDownloadClient.js
+jalview/xml/binding/uniprot/InteractantType.js
+jalview/xml/binding/uniprot/MoleculeType.js
+jalview/xml/binding/uniprot/ReactionType.js
+java/applet/AppletContext.js
+java/applet/AppletStub.js
+java/applet/JSApplet.js
+java/awt/AWTEvent.js
+java/awt/AWTEventMulticaster.js
 java/awt/AWTKeyStroke.js
+java/awt/ActiveEvent.js
+java/awt/Adjustable.js
 java/awt/AlphaComposite.js
+java/awt/BasicStroke.js
+java/awt/BorderLayout.js
 java/awt/CardLayout.js
+java/awt/Color.js
+java/awt/Component.js
+java/awt/ComponentOrientation.js
 java/awt/Composite.js
+java/awt/Container.js
 java/awt/ContainerOrderFocusTraversalPolicy.js
+java/awt/Cursor.js
 java/awt/DefaultFocusTraversalPolicy.js
 java/awt/DefaultKeyboardFocusManager.js
+java/awt/Desktop.js
+java/awt/Dialog.js
+java/awt/Dimension.js
+java/awt/EventDispatchThread.js
+java/awt/EventFilter.js
+java/awt/EventQueue.js
+java/awt/EventQueueItem.js
+java/awt/FlowLayout.js
 java/awt/FocusTraversalPolicy.js
+java/awt/Font.js
+java/awt/FontMetrics.js
+java/awt/GraphicsCallback.js
+java/awt/GraphicsConfiguration.js
+java/awt/GraphicsDevice.js
+java/awt/GraphicsEnvironment.js
 java/awt/GridBagConstraints.js
 java/awt/GridBagLayout.js
 java/awt/GridBagLayoutInfo.js
 java/awt/GridLayout.js
 java/awt/Image.js
+java/awt/Insets.js
+java/awt/ItemSelectable.js
+java/awt/JSComponent.js
+java/awt/JSDialog.js
+java/awt/JSFrame.js
+java/awt/JSPanel.js
 java/awt/KeyEventDispatcher.js
 java/awt/KeyEventPostProcessor.js
 java/awt/KeyboardFocusManager.js
+java/awt/LayoutManager.js
+java/awt/LayoutManager2.js
+java/awt/LightweightDispatcher.js
+java/awt/Paint.js
+java/awt/Point.js
+java/awt/Queue.js
+java/awt/Rectangle.js
+java/awt/RenderingHints.js
 java/awt/SentEvent.js
+java/awt/Shape.js
+java/awt/Stroke.js
+java/awt/Taskbar.js
 java/awt/TextComponent.js
+java/awt/Toolkit.js
+java/awt/Transparency.js
 java/awt/VKCollection.js
+java/awt/Window.js
+java/awt/color/ColorSpace.js
 java/awt/datatransfer/ClipboardOwner.js
-java/awt/datatransfer/DataFlavor.js
 java/awt/datatransfer/FlavorMap.js
 java/awt/datatransfer/FlavorTable.js
-java/awt/datatransfer/MimeType.js
-java/awt/datatransfer/MimeTypeParameterList.js
 java/awt/datatransfer/SystemFlavorMap.js
-java/awt/datatransfer/Transferable.js
 java/awt/dnd/DropTarget.js
 java/awt/dnd/DropTargetContext.js
-java/awt/dnd/DropTargetDropEvent.js
-java/awt/dnd/DropTargetEvent.js
 java/awt/dnd/DropTargetListener.js
-java/awt/dnd/peer/DropTargetContextPeer.js
 java/awt/dnd/peer/DropTargetPeer.js
+java/awt/event/AWTEventListener.js
 java/awt/event/ActionEvent.js
+java/awt/event/ActionListener.js
+java/awt/event/AdjustmentEvent.js
+java/awt/event/AdjustmentListener.js
 java/awt/event/ComponentAdapter.js
+java/awt/event/ComponentEvent.js
+java/awt/event/ComponentListener.js
 java/awt/event/ContainerEvent.js
+java/awt/event/ContainerListener.js
 java/awt/event/FocusAdapter.js
+java/awt/event/FocusEvent.js
+java/awt/event/FocusListener.js
+java/awt/event/HierarchyBoundsListener.js
+java/awt/event/HierarchyEvent.js
+java/awt/event/HierarchyListener.js
+java/awt/event/InputEvent.js
+java/awt/event/InputMethodListener.js
+java/awt/event/InvocationEvent.js
+java/awt/event/ItemEvent.js
+java/awt/event/ItemListener.js
 java/awt/event/KeyAdapter.js
 java/awt/event/KeyEvent.js
+java/awt/event/KeyListener.js
 java/awt/event/MouseAdapter.js
+java/awt/event/MouseEvent.js
+java/awt/event/MouseListener.js
 java/awt/event/MouseMotionAdapter.js
+java/awt/event/MouseMotionListener.js
 java/awt/event/MouseWheelEvent.js
+java/awt/event/MouseWheelListener.js
+java/awt/event/TextListener.js
+java/awt/event/WindowAdapter.js
+java/awt/event/WindowEvent.js
+java/awt/event/WindowFocusListener.js
+java/awt/event/WindowListener.js
+java/awt/event/WindowStateListener.js
+java/awt/font/FontRenderContext.js
 java/awt/font/TextAttribute.js
+java/awt/geom/AffineTransform.js
+java/awt/geom/Dimension2D.js
 java/awt/geom/Path2D.js
 java/awt/geom/PathIterator.js
+java/awt/geom/Point2D.js
 java/awt/geom/RectIterator.js
+java/awt/geom/Rectangle2D.js
+java/awt/geom/RectangularShape.js
 java/awt/image/BufferedImage.js
 java/awt/image/ColorModel.js
 java/awt/image/DataBuffer.js
 java/awt/image/DataBufferInt.js
 java/awt/image/DirectColorModel.js
 java/awt/image/ImageConsumer.js
+java/awt/image/ImageObserver.js
 java/awt/image/ImageProducer.js
 java/awt/image/PackedColorModel.js
 java/awt/image/PixelGrabber.js
@@ -494,15 +186,26 @@ java/awt/image/RenderedImage.js
 java/awt/image/SampleModel.js
 java/awt/image/SinglePixelPackedSampleModel.js
 java/awt/image/WritableRaster.js
+java/awt/peer/ComponentPeer.js
+java/awt/peer/ContainerPeer.js
 java/awt/peer/DialogPeer.js
+java/awt/peer/FramePeer.js
 java/awt/peer/KeyboardFocusManagerPeer.js
+java/awt/peer/LightweightPeer.js
+java/awt/peer/WindowPeer.js
 java/awt/print/Printable.js
+java/beans/ChangeListenerMap.js
+java/beans/PropertyChangeEvent.js
+java/beans/PropertyChangeListener.js
+java/beans/PropertyChangeSupport.js
 java/io/BufferedInputStream.js
 java/io/BufferedReader.js
 java/io/BufferedWriter.js
 java/io/ByteArrayInputStream.js
 java/io/ByteArrayOutputStream.js
 java/io/Closeable.js
+java/io/DataInput.js
+java/io/DataInputStream.js
 java/io/File.js
 java/io/FileDescriptor.js
 java/io/FileInputStream.js
@@ -518,41 +221,44 @@ java/io/ObjectStreamField.js
 java/io/OutputStream.js
 java/io/OutputStreamWriter.js
 java/io/PrintStream.js
-java/io/PrintWriter.js
-java/io/PushbackInputStream.js
 java/io/Reader.js
 java/io/StringReader.js
 java/io/StringWriter.js
 java/io/Writer.js
+java/lang/AbstractStringBuilder.js
 java/lang/AutoCloseable.js
+java/lang/Class.js
+java/lang/Enum.js
 java/lang/Iterable.js
 java/lang/Readable.js
 java/lang/Runtime.js
+java/lang/StringBuffer.js
 java/lang/StringBuilder.js
+java/lang/Thread.js
+java/lang/ThreadGroup.js
+java/lang/ThreadLocal.js
+java/lang/ref/ReferenceQueue.js
+java/lang/reflect/AccessibleObject.js
+java/lang/reflect/AnnotatedElement.js
 java/lang/reflect/Constructor.js
 java/lang/reflect/Method.js
 java/math/BigDecimal.js
 java/math/BigInteger.js
 java/math/MathContext.js
 java/math/RoundingMode.js
+java/net/ContentHandler.js
 java/net/HttpURLConnection.js
-java/net/HttpsURLConnection.js
 java/net/URI.js
+java/net/URL.js
 java/net/URLConnection.js
-java/net/URLDecoder.js
 java/net/URLStreamHandler.js
-java/nio/Bits.js
-java/nio/Buffer.js
-java/nio/ByteBuffer.js
-java/nio/ByteOrder.js
-java/nio/CharBuffer.js
-java/nio/HeapByteBuffer.js
-java/nio/HeapCharBuffer.js
-java/nio/charset/Charset.js
-java/nio/charset/CharsetDecoder.js
-java/nio/charset/CoderResult.js
-java/nio/charset/CodingErrorAction.js
-java/security/AccessControlContext.js
+java/net/URLStreamHandlerFactory.js
+java/net/UnknownHostException.js
+java/nio/file/FileSystem.js
+java/nio/file/FileSystems.js
+java/nio/file/Path.js
+java/nio/file/Watchable.js
+java/nio/file/spi/FileSystemProvider.js
 java/security/AccessController.js
 java/security/PrivilegedAction.js
 java/security/PrivilegedExceptionAction.js
@@ -568,26 +274,46 @@ java/text/Format.js
 java/text/MessageFormat.js
 java/text/NumberFormat.js
 java/text/SimpleDateFormat.js
+java/text/spi/BreakIteratorProvider.js
+java/text/spi/CollatorProvider.js
+java/text/spi/DateFormatProvider.js
+java/text/spi/DateFormatSymbolsProvider.js
+java/text/spi/DecimalFormatSymbolsProvider.js
+java/text/spi/NumberFormatProvider.js
+java/util/AbstractCollection.js
+java/util/AbstractList.js
+java/util/AbstractMap.js
 java/util/AbstractQueue.js
 java/util/AbstractSequentialList.js
+java/util/AbstractSet.js
 java/util/ArrayDeque.js
+java/util/ArrayList.js
+java/util/Arrays.js
 java/util/BitSet.js
 java/util/Calendar.js
 java/util/Collection.js
+java/util/Collections.js
 java/util/ComparableTimSort.js
 java/util/Comparator.js
 java/util/Deque.js
+java/util/Dictionary.js
 java/util/DualPivotQuicksort.js
 java/util/Enumeration.js
+java/util/EventListener.js
+java/util/EventObject.js
 java/util/Formatter.js
 java/util/GregorianCalendar.js
+java/util/HashMap.js
+java/util/HashSet.js
+java/util/Hashtable.js
+java/util/IdentityHashMap.js
 java/util/Iterator.js
 java/util/LinkedHashMap.js
 java/util/LinkedHashSet.js
 java/util/LinkedList.js
 java/util/List.js
 java/util/ListIterator.js
-java/util/ListResourceBundle.js
+java/util/Locale.js
 java/util/Map.js
 java/util/NavigableMap.js
 java/util/NavigableSet.js
@@ -595,16 +321,22 @@ java/util/Objects.js
 java/util/Properties.js
 java/util/PropertyResourceBundle.js
 java/util/Queue.js
+java/util/Random.js
 java/util/RandomAccess.js
+java/util/RandomAccessSubList.js
 java/util/ResourceBundle.js
+java/util/ServiceLoader.js
 java/util/Set.js
 java/util/SortedMap.js
 java/util/SortedSet.js
+java/util/Stack.js
 java/util/StringTokenizer.js
+java/util/SubList.js
 java/util/TimSort.js
 java/util/TimeZone.js
 java/util/TreeMap.js
 java/util/TreeSet.js
+java/util/Vector.js
 java/util/WeakHashMap.js
 java/util/concurrent/AbstractExecutorService.js
 java/util/concurrent/BlockingQueue.js
@@ -619,7 +351,6 @@ java/util/concurrent/Semaphore.js
 java/util/concurrent/ThreadFactory.js
 java/util/concurrent/ThreadPoolExecutor.js
 java/util/concurrent/TimeUnit.js
-java/util/concurrent/atomic/AtomicBoolean.js
 java/util/concurrent/atomic/AtomicInteger.js
 java/util/concurrent/locks/AbstractOwnableSynchronizer.js
 java/util/concurrent/locks/AbstractQueuedSynchronizer.js
@@ -628,14 +359,16 @@ java/util/concurrent/locks/Lock.js
 java/util/concurrent/locks/ReadWriteLock.js
 java/util/concurrent/locks/ReentrantLock.js
 java/util/concurrent/locks/ReentrantReadWriteLock.js
-java/util/function/Function.js
 java/util/jar/JarEntry.js
 java/util/jar/JarInputStream.js
-java/util/logging/Level.js
-java/util/logging/Logger.js
 java/util/regex/MatchResult.js
 java/util/regex/Matcher.js
 java/util/regex/Pattern.js
+java/util/spi/CalendarDataProvider.js
+java/util/spi/CurrencyNameProvider.js
+java/util/spi/LocaleNameProvider.js
+java/util/spi/LocaleServiceProvider.js
+java/util/spi/TimeZoneNameProvider.js
 java/util/zip/CRC32.js
 java/util/zip/Inflater.js
 java/util/zip/InflaterInputStream.js
@@ -647,17 +380,20 @@ javajs/api/GenericCifDataParser.js
 javajs/api/GenericLineReader.js
 javajs/api/GenericOutputChannel.js
 javajs/api/JSONEncodable.js
-javajs/awt/Font.js
-javajs/awt/FontManager.js
 javajs/util/A4.js
+javajs/util/AU.js
+javajs/util/AjaxURLConnection.js
 javajs/util/AjaxURLStreamHandler.js
+javajs/util/AjaxURLStreamHandlerFactory.js
 javajs/util/BS.js
 javajs/util/Base64.js
 javajs/util/CU.js
 javajs/util/CifDataParser.js
 javajs/util/DF.js
 javajs/util/Encoding.js
+javajs/util/JSThread.js
 javajs/util/LimitedLineReader.js
+javajs/util/Lst.js
 javajs/util/M3.js
 javajs/util/M34.js
 javajs/util/M4.js
@@ -665,96 +401,141 @@ javajs/util/OC.js
 javajs/util/P3.js
 javajs/util/P3i.js
 javajs/util/P4.js
+javajs/util/PT.js
 javajs/util/Rdr.js
 javajs/util/SB.js
 javajs/util/T3.js
 javajs/util/T3i.js
 javajs/util/T4.js
 javajs/util/V3.js
+javax/net/ssl/HttpsURLConnection.js
 javax/swing/AbstractAction.js
-javax/swing/AbstractCellEditor.js
-javax/swing/AbstractSpinnerModel.js
+javax/swing/AbstractButton.js
+javax/swing/AbstractListModel.js
 javax/swing/Action.js
+javax/swing/ActionMap.js
+javax/swing/AncestorNotifier.js
+javax/swing/ArrayTable.js
 javax/swing/Autoscroller.js
-javax/swing/CellEditor.js
+javax/swing/BorderFactory.js
+javax/swing/BoundedRangeModel.js
+javax/swing/BoxLayout.js
+javax/swing/ButtonGroup.js
+javax/swing/ButtonModel.js
 javax/swing/CellRendererPane.js
-javax/swing/ColorChooserDialog.js
+javax/swing/ClientPropertyKey.js
+javax/swing/ComboBoxEditor.js
+javax/swing/ComboBoxModel.js
 javax/swing/ComponentInputMap.js
-javax/swing/DefaultCellEditor.js
+javax/swing/DefaultBoundedRangeModel.js
+javax/swing/DefaultButtonModel.js
+javax/swing/DefaultComboBoxModel.js
 javax/swing/DefaultDesktopManager.js
 javax/swing/DefaultListCellRenderer.js
 javax/swing/DefaultListSelectionModel.js
 javax/swing/DefaultRowSorter.js
+javax/swing/DefaultSingleSelectionModel.js
 javax/swing/DesktopManager.js
 javax/swing/DropMode.js
+javax/swing/FocusManager.js
 javax/swing/Icon.js
 javax/swing/ImageIcon.js
 javax/swing/InputMap.js
-javax/swing/JColorChooser.js
+javax/swing/InternalFrameFocusTraversalPolicy.js
+javax/swing/JApplet.js
+javax/swing/JButton.js
+javax/swing/JCheckBox.js
+javax/swing/JCheckBoxMenuItem.js
+javax/swing/JComboBox.js
+javax/swing/JComponent.js
 javax/swing/JDesktopPane.js
 javax/swing/JDialog.js
-javax/swing/JFileChooser.js
-javax/swing/JFormattedTextField.js
+javax/swing/JFrame.js
 javax/swing/JInternalFrame.js
+javax/swing/JLabel.js
+javax/swing/JLayeredPane.js
 javax/swing/JList.js
+javax/swing/JMenu.js
+javax/swing/JMenuBar.js
+javax/swing/JMenuItem.js
 javax/swing/JOptionPane.js
+javax/swing/JPanel.js
+javax/swing/JPopupMenu.js
 javax/swing/JProgressBar.js
-javax/swing/JRadioButton.js
+javax/swing/JRadioButtonMenuItem.js
+javax/swing/JRootPane.js
+javax/swing/JScrollBar.js
 javax/swing/JScrollPane.js
-javax/swing/JSlider.js
-javax/swing/JSpinner.js
+javax/swing/JSeparator.js
 javax/swing/JTabbedPane.js
 javax/swing/JTable.js
 javax/swing/JTextArea.js
 javax/swing/JTextField.js
+javax/swing/JToggleButton.js
 javax/swing/JToolTip.js
 javax/swing/JViewport.js
 javax/swing/JWindow.js
 javax/swing/KeyStroke.js
+javax/swing/KeyboardManager.js
+javax/swing/LayoutComparator.js
+javax/swing/LayoutFocusTraversalPolicy.js
 javax/swing/ListCellRenderer.js
+javax/swing/ListModel.js
 javax/swing/ListSelectionModel.js
+javax/swing/LookAndFeel.js
+javax/swing/MenuElement.js
 javax/swing/MenuSelectionManager.js
+javax/swing/MutableComboBoxModel.js
 javax/swing/Popup.js
 javax/swing/PopupFactory.js
-javax/swing/RowFilter.js
+javax/swing/RepaintManager.js
+javax/swing/RootPaneContainer.js
 javax/swing/RowSorter.js
 javax/swing/ScrollPaneConstants.js
 javax/swing/ScrollPaneLayout.js
 javax/swing/Scrollable.js
+javax/swing/SingleSelectionModel.js
+javax/swing/SizeRequirements.js
 javax/swing/SortOrder.js
-javax/swing/SpinnerModel.js
-javax/swing/SpinnerNumberModel.js
-javax/swing/Spring.js
-javax/swing/SpringLayout.js
+javax/swing/SortingFocusTraversalPolicy.js
+javax/swing/SwingConstants.js
+javax/swing/SwingContainerOrderFocusTraversalPolicy.js
+javax/swing/SwingDefaultFocusTraversalPolicy.js
+javax/swing/SwingPaintEventDispatcher.js
+javax/swing/SwingUtilities.js
 javax/swing/Timer.js
 javax/swing/ToolTipManager.js
+javax/swing/TransferHandler.js
+javax/swing/UIDefaults.js
+javax/swing/UIManager.js
 javax/swing/ViewportLayout.js
+javax/swing/WindowConstants.js
+javax/swing/border/AbstractBorder.js
+javax/swing/border/BevelBorder.js
+javax/swing/border/Border.js
 javax/swing/border/CompoundBorder.js
+javax/swing/border/EmptyBorder.js
+javax/swing/border/EtchedBorder.js
 javax/swing/border/LineBorder.js
-javax/swing/border/MatteBorder.js
-javax/swing/border/TitledBorder.js
-javax/swing/colorchooser/AbstractColorChooserPanel.js
-javax/swing/colorchooser/CenterLayout.js
-javax/swing/colorchooser/ColorChooserComponentFactory.js
-javax/swing/colorchooser/ColorSelectionModel.js
-javax/swing/colorchooser/DefaultColorSelectionModel.js
-javax/swing/colorchooser/DefaultPreviewPanel.js
-javax/swing/colorchooser/DefaultRGBChooserPanel.js
-javax/swing/colorchooser/DefaultSwatchChooserPanel.js
-javax/swing/colorchooser/MainSwatchPanel.js
-javax/swing/colorchooser/RecentSwatchPanel.js
-javax/swing/colorchooser/SmartGridLayout.js
-javax/swing/colorchooser/SwatchPanel.js
+javax/swing/event/AncestorEvent.js
+javax/swing/event/AncestorListener.js
 javax/swing/event/CaretEvent.js
 javax/swing/event/CaretListener.js
 javax/swing/event/CellEditorListener.js
+javax/swing/event/ChangeEvent.js
+javax/swing/event/ChangeListener.js
 javax/swing/event/DocumentEvent.js
 javax/swing/event/DocumentListener.js
+javax/swing/event/EventListenerList.js
+javax/swing/event/HyperlinkListener.js
 javax/swing/event/InternalFrameAdapter.js
 javax/swing/event/InternalFrameEvent.js
 javax/swing/event/InternalFrameListener.js
+javax/swing/event/ListDataEvent.js
+javax/swing/event/ListDataListener.js
 javax/swing/event/ListSelectionEvent.js
 javax/swing/event/ListSelectionListener.js
+javax/swing/event/MenuEvent.js
 javax/swing/event/MenuKeyListener.js
 javax/swing/event/MenuListener.js
 javax/swing/event/MouseInputListener.js
@@ -767,17 +548,23 @@ javax/swing/event/TableModelEvent.js
 javax/swing/event/TableModelListener.js
 javax/swing/event/UndoableEditEvent.js
 javax/swing/event/UndoableEditListener.js
-javax/swing/filechooser/FileFilter.js
-javax/swing/filechooser/FileView.js
+javax/swing/plaf/ActionMapUIResource.js
+javax/swing/plaf/BorderUIResource.js
+javax/swing/plaf/ColorUIResource.js
 javax/swing/plaf/ComponentInputMapUIResource.js
+javax/swing/plaf/ComponentUI.js
+javax/swing/plaf/DimensionUIResource.js
+javax/swing/plaf/FontUIResource.js
 javax/swing/plaf/InputMapUIResource.js
+javax/swing/plaf/InsetsUIResource.js
+javax/swing/plaf/UIResource.js
 javax/swing/plaf/basic/BasicBorders.js
+javax/swing/plaf/basic/BasicGraphicsUtils.js
 javax/swing/table/AbstractTableModel.js
 javax/swing/table/DefaultTableCellRenderer.js
 javax/swing/table/DefaultTableColumnModel.js
 javax/swing/table/DefaultTableModel.js
 javax/swing/table/JTableHeader.js
-javax/swing/table/TableCellEditor.js
 javax/swing/table/TableCellRenderer.js
 javax/swing/table/TableColumn.js
 javax/swing/table/TableColumnModel.js
@@ -785,21 +572,18 @@ javax/swing/table/TableModel.js
 javax/swing/table/TableRowSorter.js
 javax/swing/text/AbstractDocument.js
 javax/swing/text/AttributeSet.js
+javax/swing/text/BoxView.js
 javax/swing/text/Caret.js
+javax/swing/text/CompositeView.js
 javax/swing/text/DefaultCaret.js
 javax/swing/text/DefaultEditorKit.js
-javax/swing/text/DefaultFormatter.js
-javax/swing/text/DefaultFormatterFactory.js
 javax/swing/text/Document.js
-javax/swing/text/DocumentFilter.js
 javax/swing/text/EditorKit.js
 javax/swing/text/Element.js
 javax/swing/text/GapContent.js
 javax/swing/text/GapVector.js
-javax/swing/text/InternationalFormatter.js
 javax/swing/text/JTextComponent.js
 javax/swing/text/MutableAttributeSet.js
-javax/swing/text/NumberFormatter.js
 javax/swing/text/PlainDocument.js
 javax/swing/text/PlainView.js
 javax/swing/text/Position.js
@@ -813,7 +597,7 @@ javax/swing/text/TabExpander.js
 javax/swing/text/TextAction.js
 javax/swing/text/Utilities.js
 javax/swing/text/View.js
-javax/swing/tree/TreeCellEditor.js
+javax/swing/text/WrappedPlainView.js
 javax/swing/tree/TreeNode.js
 javax/swing/undo/AbstractUndoableEdit.js
 javax/swing/undo/CompoundEdit.js
@@ -831,15 +615,11 @@ javax/xml/bind/annotation/adapters/CollapsedStringAdapter.js
 javax/xml/bind/annotation/adapters/XmlAdapter.js
 javax/xml/bind/helpers/AbstractUnmarshallerImpl.js
 javax/xml/bind/helpers/DefaultValidationEventHandler.js
-javax/xml/datatype/DatatypeFactory.js
 javax/xml/datatype/XMLGregorianCalendar.js
 javax/xml/namespace/QName.js
 javax/xml/stream/XMLInputFactory.js
+javax/xml/stream/XMLStreamConstants.js
 javax/xml/stream/XMLStreamReader.js
-mc_view/Atom.js
-mc_view/Bond.js
-mc_view/PDBChain.js
-mc_view/Residue.js
 net/miginfocom/layout/AC.js
 net/miginfocom/layout/AnimSpec.js
 net/miginfocom/layout/BoundSize.js
@@ -858,7 +638,6 @@ net/miginfocom/layout/UnitValue.js
 net/miginfocom/swing/MigLayout.js
 net/miginfocom/swing/SwingComponentWrapper.js
 net/miginfocom/swing/SwingContainerWrapper.js
-org/apache/xerces/jaxp/datatype/DatatypeFactoryImpl.js
 org/apache/xerces/jaxp/datatype/XMLGregorianCalendarImpl.js
 org/jmol/adapter/readers/cif/CifReader.js
 org/jmol/adapter/readers/cif/MMCifReader.js
@@ -874,9 +653,9 @@ org/jmol/adapter/smarter/Resolver.js
 org/jmol/adapter/smarter/SmarterJmolAdapter.js
 org/jmol/adapter/smarter/Structure.js
 org/jmol/adapter/smarter/StructureIterator.js
-org/jmol/adapter/smarter/XtalSymmetry.js
 org/jmol/api/AtomIndexIterator.js
 org/jmol/api/EventManager.js
+org/jmol/api/FontManager.js
 org/jmol/api/GenericFileInterface.js
 org/jmol/api/GenericMouseInterface.js
 org/jmol/api/GenericPlatform.js
@@ -886,9 +665,9 @@ org/jmol/api/JmolAdapterAtomIterator.js
 org/jmol/api/JmolAdapterBondIterator.js
 org/jmol/api/JmolAdapterStructureIterator.js
 org/jmol/api/JmolCallbackListener.js
+org/jmol/api/JmolFilesReaderInterface.js
 org/jmol/api/JmolGraphicsInterface.js
 org/jmol/api/JmolMeasurementClient.js
-org/jmol/api/JmolPropertyManager.js
 org/jmol/api/JmolRendererInterface.js
 org/jmol/api/JmolRepaintManager.js
 org/jmol/api/JmolScriptEvaluator.js
@@ -920,6 +699,8 @@ org/jmol/c/PAL.js
 org/jmol/c/STER.js
 org/jmol/c/STR.js
 org/jmol/c/VDW.js
+org/jmol/dssx/Bridge.js
+org/jmol/dssx/DSSP.js
 org/jmol/g3d/CylinderRenderer.js
 org/jmol/g3d/G3DRenderer.js
 org/jmol/g3d/Graphics3D.js
@@ -936,6 +717,7 @@ org/jmol/i18n/GT.js
 org/jmol/i18n/Language.js
 org/jmol/i18n/Resource.js
 org/jmol/io/FileReader.js
+org/jmol/io/FilesReader.js
 org/jmol/modelset/Atom.js
 org/jmol/modelset/AtomCollection.js
 org/jmol/modelset/AtomIteratorWithinModel.js
@@ -945,9 +727,11 @@ org/jmol/modelset/BondIterator.js
 org/jmol/modelset/BondIteratorSelected.js
 org/jmol/modelset/Chain.js
 org/jmol/modelset/Group.js
+org/jmol/modelset/HBond.js
 org/jmol/modelset/LabelToken.js
 org/jmol/modelset/Measurement.js
 org/jmol/modelset/MeasurementData.js
+org/jmol/modelset/MeasurementPending.js
 org/jmol/modelset/Model.js
 org/jmol/modelset/ModelLoader.js
 org/jmol/modelset/ModelSet.js
@@ -965,12 +749,9 @@ org/jmol/modelsetbio/BioPolymer.js
 org/jmol/modelsetbio/BioResolver.js
 org/jmol/modelsetbio/Helix.js
 org/jmol/modelsetbio/Monomer.js
-org/jmol/modelsetbio/NucleicMonomer.js
-org/jmol/modelsetbio/NucleicPolymer.js
-org/jmol/modelsetbio/PhosphorusMonomer.js
-org/jmol/modelsetbio/PhosphorusPolymer.js
 org/jmol/modelsetbio/ProteinStructure.js
 org/jmol/modelsetbio/Sheet.js
+org/jmol/modelsetbio/Turn.js
 org/jmol/render/BallsRenderer.js
 org/jmol/render/BbcageRenderer.js
 org/jmol/render/CageRenderer.js
@@ -1000,6 +781,7 @@ org/jmol/script/ScriptFunction.js
 org/jmol/script/ScriptManager.js
 org/jmol/script/ScriptMathProcessor.js
 org/jmol/script/ScriptParam.js
+org/jmol/script/ScriptQueueThread.js
 org/jmol/script/ScriptTokenParser.js
 org/jmol/script/T.js
 org/jmol/scriptext/CmdExt.js
@@ -1008,6 +790,7 @@ org/jmol/scriptext/ScriptExt.js
 org/jmol/shape/AtomShape.js
 org/jmol/shape/Balls.js
 org/jmol/shape/Bbcage.js
+org/jmol/shape/Echo.js
 org/jmol/shape/FontLineShape.js
 org/jmol/shape/Frank.js
 org/jmol/shape/Hover.js
@@ -1027,6 +810,7 @@ org/jmol/symmetry/SymmetryInfo.js
 org/jmol/symmetry/UnitCell.js
 org/jmol/thread/HoverWatcherThread.js
 org/jmol/thread/JmolThread.js
+org/jmol/thread/TimeoutThread.js
 org/jmol/util/BSUtil.js
 org/jmol/util/BoxInfo.js
 org/jmol/util/C.js
@@ -1036,6 +820,7 @@ org/jmol/util/DefaultLogger.js
 org/jmol/util/Edge.js
 org/jmol/util/Elements.js
 org/jmol/util/Escape.js
+org/jmol/util/Font.js
 org/jmol/util/GData.js
 org/jmol/util/Geodesic.js
 org/jmol/util/Int2IntHash.js
@@ -1063,7 +848,6 @@ org/jmol/viewer/JC.js
 org/jmol/viewer/ModelManager.js
 org/jmol/viewer/MotionPoint.js
 org/jmol/viewer/MouseState.js
-org/jmol/viewer/PropertyManager.js
 org/jmol/viewer/SelectionManager.js
 org/jmol/viewer/ShapeManager.js
 org/jmol/viewer/StateManager.js
@@ -1072,6 +856,7 @@ org/jmol/viewer/TransformManager.js
 org/jmol/viewer/Viewer.js
 org/jmol/viewer/binding/Binding.js
 org/jmol/viewer/binding/JmolBinding.js
+org/json/simple/parser/ParseException.js
 org/xml/sax/AttributeList.js
 org/xml/sax/Attributes.js
 org/xml/sax/ContentHandler.js
@@ -1079,11 +864,18 @@ org/xml/sax/InputSource.js
 org/xml/sax/Parser.js
 org/xml/sax/XMLReader.js
 org/xml/sax/ext/Attributes2.js
-sun/awt/AWTAccessor.js
+sun/awt/AWTAutoShutdown.js
+sun/awt/AppContext.js
 sun/awt/CausedFocusEvent.js
+sun/awt/ComponentFactory.js
 sun/awt/EventQueueItem.js
-sun/awt/KeyboardFocusManagerPeerProvider.js
+sun/awt/MostRecentKeyValue.js
+sun/awt/MostRecentThreadAppContext.js
+sun/awt/PaintEventDispatcher.js
+sun/awt/PostEventQueue.js
+sun/awt/RequestFocusController.js
 sun/awt/SunGraphicsCallback.js
+sun/awt/SunToolkit.js
 sun/awt/image/DataStealer.js
 sun/awt/image/IntegerComponentRaster.js
 sun/awt/image/IntegerInterleavedRaster.js
@@ -1094,20 +886,18 @@ sun/font/EAttribute.js
 sun/font/FontDesignMetrics.js
 sun/java2d/StateTrackable.js
 sun/java2d/StateTrackableDelegate.js
-sun/nio/cs/ArrayDecoder.js
-sun/nio/cs/HistoricallyNamedCharset.js
-sun/nio/cs/StandardCharsets.js
-sun/nio/cs/ThreadLocalCoders.js
-sun/nio/cs/UTF_8.js
-sun/nio/cs/Unicode.js
+sun/net/www/MessageHeader.js
 sun/swing/DefaultLookup.js
 sun/swing/StringUIClientPropertyKey.js
+sun/swing/SwingLazyValue.js
 sun/swing/SwingUtilities2.js
 sun/swing/UIAction.js
 sun/swing/UIClientPropertyKey.js
 sun/swing/table/DefaultTableCellHeaderRenderer.js
 sun/text/resources/FormatData.js
-sun/text/resources/FormatData_en.js
+sun/text/resources/en/FormatData_en.js
+sun/text/resources/en/FormatData_en_GB.js
+sun/text/resources/en/FormatData_en_US.js
 sun/util/calendar/AbstractCalendar.js
 sun/util/calendar/BaseCalendar.js
 sun/util/calendar/CalendarDate.js
@@ -1115,22 +905,56 @@ sun/util/calendar/CalendarSystem.js
 sun/util/calendar/CalendarUtils.js
 sun/util/calendar/Gregorian.js
 sun/util/calendar/ZoneInfo.js
+sun/util/calendar/ZoneInfoFile.js
+sun/util/locale/BaseLocale.js
+sun/util/locale/LanguageTag.js
+sun/util/locale/LocaleUtils.js
+sun/util/locale/provider/AuxLocaleProviderAdapter.js
+sun/util/locale/provider/AvailableLanguageTags.js
+sun/util/locale/provider/CalendarDataProviderImpl.js
+sun/util/locale/provider/CalendarDataUtility.js
+sun/util/locale/provider/CalendarProviderImpl.js
+sun/util/locale/provider/DateFormatProviderImpl.js
+sun/util/locale/provider/JRELocaleProviderAdapter.js
+sun/util/locale/provider/LocaleDataMetaInfo.js
+sun/util/locale/provider/LocaleProviderAdapter.js
+sun/util/locale/provider/LocaleResources.js
+sun/util/locale/provider/LocaleServiceProviderPool.js
+sun/util/locale/provider/ResourceBundleBasedAdapter.js
+sun/util/locale/provider/SPILocaleProviderAdapter.js
 sun/util/resources/LocaleData.js
-swingjs/JSAbstractDocument.js
-swingjs/JSCharSet.js
-swingjs/JSDnD.js
-swingjs/JSDocumentEvent.js
+sun/util/resources/ParallelListResourceBundle.js
+sun/util/spi/CalendarProvider.js
+swingjs/JSApp.js
+swingjs/JSAppletThread.js
+swingjs/JSAppletViewer.js
+swingjs/JSDummyApplet.js
+swingjs/JSFileSystem.js
 swingjs/JSFocusPeer.js
-swingjs/JSGraphicsCompositor.js
+swingjs/JSFontMetrics.js
+swingjs/JSFrameViewer.js
+swingjs/JSGraphics2D.js
+swingjs/JSGraphicsConfiguration.js
+swingjs/JSGraphicsEnvironment.js
 swingjs/JSImage.js
 swingjs/JSImagekit.js
 swingjs/JSKeyEvent.js
 swingjs/JSMenuManager.js
-swingjs/JSPlainDocument.js
-swingjs/JSTempFile.js
+swingjs/JSMouse.js
+swingjs/JSScreenDevice.js
+swingjs/JSThreadGroup.js
+swingjs/JSToolkit.js
+swingjs/JSUtil.js
 swingjs/a2s/A2SContainer.js
 swingjs/a2s/Dialog.js
-swingjs/api/JSMinimalAbstractDocument.js
+swingjs/api/Interface.js
+swingjs/api/JSUtilI.js
+swingjs/api/js/DOMNode.js
+swingjs/api/js/HTML5Canvas.js
+swingjs/api/js/HTML5CanvasContext2D.js
+swingjs/api/js/JSFunction.js
+swingjs/api/js/JSInterface.js
+swingjs/jquery/JQueryUI.js
 swingjs/json/JSON.js
 swingjs/jzlib/Adler32.js
 swingjs/jzlib/CRC32.js
@@ -1142,42 +966,66 @@ swingjs/jzlib/Inflate.js
 swingjs/jzlib/Inflater.js
 swingjs/jzlib/InflaterInputStream.js
 swingjs/jzlib/ZStream.js
-swingjs/plaf/BasicArrowButton.js
+swingjs/plaf/BasicComboBoxEditor.js
+swingjs/plaf/BasicComboBoxRenderer.js
 swingjs/plaf/BasicHTML.js
+swingjs/plaf/ButtonListener.js
 swingjs/plaf/CellHolder.js
-swingjs/plaf/CenterLayout.js
+swingjs/plaf/DefaultMenuLayout.js
+swingjs/plaf/HTML5LookAndFeel.js
 swingjs/plaf/JSAppletUI.js
-swingjs/plaf/JSColorChooserUI.js
+swingjs/plaf/JSButtonUI.js
+swingjs/plaf/JSCheckBoxMenuItemUI.js
+swingjs/plaf/JSCheckBoxUI.js
+swingjs/plaf/JSComboBoxUI.js
+swingjs/plaf/JSComboPopupList.js
+swingjs/plaf/JSComponentUI.js
 swingjs/plaf/JSDesktopIconUI.js
 swingjs/plaf/JSDesktopPaneUI.js
 swingjs/plaf/JSDialogUI.js
-swingjs/plaf/JSFormattedTextFieldUI.js
+swingjs/plaf/JSEventHandler.js
+swingjs/plaf/JSFrameUI.js
 swingjs/plaf/JSGraphicsUtils.js
 swingjs/plaf/JSInternalFrameUI.js
+swingjs/plaf/JSLabelUI.js
+swingjs/plaf/JSLayeredPaneUI.js
+swingjs/plaf/JSLightweightUI.js
 swingjs/plaf/JSListUI.js
+swingjs/plaf/JSMenuBarUI.js
+swingjs/plaf/JSMenuItemUI.js
+swingjs/plaf/JSMenuUI.js
+swingjs/plaf/JSOptionPaneUI.js
+swingjs/plaf/JSPanelUI.js
+swingjs/plaf/JSPopupMenuSeparatorUI.js
+swingjs/plaf/JSPopupMenuUI.js
 swingjs/plaf/JSPopupUI.js
 swingjs/plaf/JSProgressBarUI.js
+swingjs/plaf/JSRadioButtonMenuItemUI.js
+swingjs/plaf/JSRadioButtonUI.js
+swingjs/plaf/JSRootPaneUI.js
+swingjs/plaf/JSScrollBarUI.js
 swingjs/plaf/JSScrollPaneUI.js
-swingjs/plaf/JSSpinnerUI.js
+swingjs/plaf/JSSeparatorUI.js
+swingjs/plaf/JSSliderUI.js
 swingjs/plaf/JSTabbedPaneUI.js
 swingjs/plaf/JSTableHeaderUI.js
 swingjs/plaf/JSTableUI.js
 swingjs/plaf/JSTextAreaUI.js
 swingjs/plaf/JSTextFieldUI.js
 swingjs/plaf/JSTextUI.js
-swingjs/plaf/JSTextViewUI.js
 swingjs/plaf/JSToolTipUI.js
 swingjs/plaf/JSViewportUI.js
+swingjs/plaf/JSWindowUI.js
+swingjs/plaf/LazyActionMap.js
+swingjs/plaf/Resizer.js
 swingjs/plaf/TextListener.js
 swingjs/xml/JSJAXBClass.js
 swingjs/xml/JSJAXBContext.js
 swingjs/xml/JSJAXBContextFactory.js
-swingjs/xml/JSJAXBDatatypeFactory.js
 swingjs/xml/JSJAXBField.js
 swingjs/xml/JSJAXBUnmarshaller.js
 swingjs/xml/JSSAXAttributes.js
 swingjs/xml/JSSAXParser.js
 swingjs/xml/JSXMLGregorianCalendarImpl.js
 swingjs/xml/JSXMLInputFactory.js
-swingjs/xml/JSXMLStreamReader.jscom/stevesoft/pat/Any.js
-swingjs/xml/JSXMLStreamReader.jscom/stevesoft/pat/Boundary.js
+swingjs/xml/JSXMLStreamReader.js
index 94bd28e..65732a4 100644 (file)
Binary files a/utils/jalviewjs/libjs/jmol-app.zip and b/utils/jalviewjs/libjs/jmol-app.zip differ
diff --git a/utils/mk_jalview_icons.sh b/utils/mk_jalview_icons.sh
new file mode 100755 (executable)
index 0000000..4a2768e
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+FILENAME512=$1
+BASENAME=${FILENAME512%-512.png}
+
+usage () {
+  echo "$(basename "$0") <LOGO_NAME>-512.png"
+}
+
+[ "$1" = "-h" ] && usage && exit 0
+[ -r "$FILENAME512" ] || ( usage && echo "'${FILENAME512}' must exist and be readable" && exit 1 )
+[ "$BASENAME" = "$FILENAME512" ] && usage && echo "Must have '-512.png' at end of filename" && exit 2
+
+set -e
+
+SIZES="16 32 38 48 64 128 256"
+declare -a ICOFILES=()
+declare -a ICNSFILES=()
+for n in $SIZES
+do
+  NEWFILE="${BASENAME}-${n}.png"
+  [ -e "{$NEWFILE}" ] && continue
+  convert -geometry "${n}x${n}" "${BASENAME}-512.png" "${NEWFILE}"
+  [ "${n}" != 38 ] && ICOFILES=( "${ICOFILES[@]}" "${NEWFILE}" )
+  [ "${n}" != 64 ] && [ "${n}" != 38 ] && ICNSFILES=( "${ICNSFILES[@]}" "${NEWFILE}" )
+done
+
+# make the .ico
+ICOFILE="${BASENAME}.ico"
+echo "Creating ${ICOFILE}"
+convert "${ICOFILES[@]}" "${ICOFILE}"
+
+# make the .icns
+ICNSFILE="${BASENAME}.icns"
+echo "Creating ${ICNSFILE}"
+png2icns "${ICNSFILE}" "${ICNSFILES[@]}"
+
+# make the rotatable icon
+ROTATABLEFILE="rotatable_${BASENAME}-38.png"
+convert "${BASENAME}-38.png" -gravity center -background white -extent 60x60 "${ROTATABLEFILE}"
+
+exit 0
diff --git a/utils/osx_signing/README b/utils/osx_signing/README
new file mode 100644 (file)
index 0000000..057b5b8
--- /dev/null
@@ -0,0 +1,32 @@
+Signing and Notarizing install4j DMGs for OSX
+
+0. You will need an up to date Apple Developer ID subscription and have a valid developer key for signing/notarizing apps, installers and DMGs available on your system.
+
+1. Build the install4j installers - signing these for windows requires a Certum cryptokey or other suitable java codesigning cert. Details to be provided.
+
+2. Unpack the OSX installer to a local directory
+hdiutil attach build/install4j/11/Jalview_Develop-2_11_2_0dev-d20210128-macos-java_11.dmg 
+mkdir newdmg; ditto /Volumes/Jalview\ Develop\ Installer newdmg/
+
+3. Remove the uninstaller if necessary/and/or others, and then deep sign the dmg
+
+xattr -cr ./newdmg/Jalview\ Develop.app/Contents/Resources/app/jre/Contents/MacOS/libjli.dylib 
+codesign --verify --deep -v ./newdmg/Jalview\ Develop.app/Contents/Resources/app/jre/Contents/MacOS/libjli.dylib 
+
+codesign --force --deep -vvvv -s "Developer ID" --options runtime --entitlements ./utils/osx_signing/entitlements.txt ./newdmg/Jalview\ Develop.app/Contents/Resources/app/jre/Contents/MacOS/libjli.dylib 
+
+codesign --verify --deep -v ./newdmg/Jalview\ Develop.app/Contents/Resources/app/jre/Contents/MacOS/libjli.dylib 
+
+codesign --force --deep -vvvv -s "Developer ID" --options runtime --entitlements ./utils/osx_signing/entitlements.txt ./newdmg/Jalview\ Develop.app/Contents/MacOS/JavaApplicationStub 
+
+hdiutil create -megabytes 240 -srcfolder ./newdmg -volname 'Jalview Develop Installer (2.11.2)' Jalview_Develop-2_11_2-macos-java_11.dmg
+
+codesign --force --deep -vvvv -s "Developer ID" --options runtime --entitlements ./utils/osx_signing/entitlements.txt Jalview_Develop-2_11_2-macos-java_11.dmg
+
+codesign --deep -vvvv Jalview_Develop-2_11_2-macos-java_11.dmg
+
+4. Notarize
+xcrun altool --notarize-app --primary-bundle-id "org.jalview.jalview-desktop" -u jalview-dev-owner@jalview.org -p $ALTOOL_PASSWORD --file Jalview_Develop-2_11_2-macos-java_11.dmg 
+.. run with --notarization-info $notarization-session-id until complete
+
+5. Staple to dmg so it can be verified without a net connection.
diff --git a/utils/osx_signing/entitlements.txt b/utils/osx_signing/entitlements.txt
new file mode 100644 (file)
index 0000000..1446982
--- /dev/null
@@ -0,0 +1,24 @@
+<?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>com.apple.security.app-sandbox</key>
+        <false/>
+        <key>com.apple.security.network.server</key>
+        <true/>
+        <key>com.apple.security.network.client</key>
+        <true/>
+        <key>com.apple.security.files.user-selected.read-write</key>
+        <true/>
+        <key>com.apple.security.cs.allow-jit</key>
+        <true/>
+        <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
+        <true/>
+        <key>com.apple.security.cs.disable-executable-page-protection</key>
+        <true/>
+        <key>com.apple.security.cs.disable-library-validation</key>
+        <true/>
+        <key>com.apple.security.cs.allow-dyld-environment-variables</key>
+        <true/>
+    </dict>
+</plist>